{"_id":"@ai4gensteam/dsh-web-search","_rev":"4-be23637dfe96a040cd48b7c761211e6a","name":"@ai4gensteam/dsh-web-search","dist-tags":{"latest":"0.3.2"},"versions":{"0.2.0":{"name":"@ai4gensteam/dsh-web-search","version":"0.2.0","keywords":["dsh","dsh-plugin","deepseek-harness","web-search","serper","tavily","brave-search"],"license":"MIT","_id":"@ai4gensteam/dsh-web-search@0.2.0","maintainers":[{"name":"ai4gensteam","email":"hello.junjie.duan@gmail.com"}],"homepage":"https://github.com/genesis-agents/dsh-plugins/tree/main/dsh-web-search-serper#readme","bugs":{"url":"https://github.com/genesis-agents/dsh-plugins/issues"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-runtime"],"platform":"web"}},"dist":{"shasum":"0899a9d617e508b40ba354c94556e6f240909bca","tarball":"https://registry.npmjs.org/@ai4gensteam/dsh-web-search/-/dsh-web-search-0.2.0.tgz","fileCount":10,"integrity":"sha512-zg3Xmh+6VLKte6JMCRr/bNH9J5s0oVODOl3mw4w0Pz6O8eKBIgT4o+aOsunDtopKS5Th39RBUeiArdlwDNBNJg==","signatures":[{"sig":"MEQCICtkxFJOIkiPCqJSRBE/Ax9TInO97N0B+kFJIBl+fvvmAiA8OumBTf8hV+BDbrDtYSsHyD/kNmkvkIldETFSyo3Rlg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":54271},"main":"lib/index.js","type":"module","engines":{"node":">=22"},"exports":{".":{"default":"./lib/index.js"},"./client":{"default":"./lib/client.js"},"./package.json":"./package.json"},"gitHead":"85eddb09ab71c2910a467c4ef63a52c8f2b0f8e1","_npmUser":{"name":"ai4gensteam","email":"hello.junjie.duan@gmail.com"},"repository":{"url":"git+https://github.com/genesis-agents/dsh-plugins.git","type":"git","directory":"dsh-web-search-serper"},"_npmVersion":"11.6.2","description":"Web search for the DeepSeek Harness ctx.web seam, over interchangeable backends: Serper, Tavily, and Brave","directories":{},"_nodeVersion":"24.12.0","dependencies":{"@deepseek-ai/schemastery":"^3.18.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/dsh-web-search_0.2.0_1787517591181_0.3225828120502481","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@ai4gensteam/dsh-web-search","version":"0.3.0","keywords":["dsh","dsh-plugin","deepseek-harness","web-search","serper","tavily","brave-search"],"license":"MIT","_id":"@ai4gensteam/dsh-web-search@0.3.0","maintainers":[{"name":"ai4gensteam","email":"hello.junjie.duan@gmail.com"}],"homepage":"https://github.com/genesis-agents/dsh-plugins/tree/main/dsh-web-search-serper#readme","bugs":{"url":"https://github.com/genesis-agents/dsh-plugins/issues"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-runtime"],"platform":"web"}},"dist":{"shasum":"c893b68b1b519e11246bdaf1517092c5e5dc8acc","tarball":"https://registry.npmjs.org/@ai4gensteam/dsh-web-search/-/dsh-web-search-0.3.0.tgz","fileCount":11,"integrity":"sha512-oh/zK23lzmJFTEE6GwR0Sh4bBPluWctP567p640QIvxcmOqH3DEOezvDH1Cn5hiieZNdaaQaCIs0qvMp+dMHOA==","signatures":[{"sig":"MEYCIQDtWnwosnIDegqZXS9seIzIHiOw5ZxIVQCCyI+j+agArQIhAKlaQ1mTe3QViVdVdl1jh5tHyu4ZqVms8cpQ+sSWDYXw","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":60358},"main":"lib/index.js","type":"module","engines":{"node":">=22"},"exports":{".":{"default":"./lib/index.js"},"./client":{"default":"./lib/client.js"},"./package.json":"./package.json"},"gitHead":"daeb67261c16195d95ccf0600acc3a534f3ec926","_npmUser":{"name":"ai4gensteam","email":"hello.junjie.duan@gmail.com"},"repository":{"url":"git+https://github.com/genesis-agents/dsh-plugins.git","type":"git","directory":"dsh-web-search-serper"},"_npmVersion":"11.6.2","description":"Web search for the DeepSeek Harness ctx.web seam, over interchangeable backends: Serper, Tavily, and Brave","directories":{},"_nodeVersion":"24.12.0","dependencies":{"@deepseek-ai/schemastery":"^3.18.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/dsh-web-search_0.3.0_1787519101649_0.976584435741435","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@ai4gensteam/dsh-web-search","version":"0.3.1","keywords":["dsh","dsh-plugin","deepseek-harness","web-search","serper","tavily","brave-search"],"license":"MIT","_id":"@ai4gensteam/dsh-web-search@0.3.1","maintainers":[{"name":"ai4gensteam","email":"hello.junjie.duan@gmail.com"}],"homepage":"https://github.com/genesis-agents/dsh-plugins/tree/main/dsh-web-search-serper#readme","bugs":{"url":"https://github.com/genesis-agents/dsh-plugins/issues"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-runtime"],"platform":"web"}},"dist":{"shasum":"a7ae1beeb37ecf8fef47c4d208f734e5b608face","tarball":"https://registry.npmjs.org/@ai4gensteam/dsh-web-search/-/dsh-web-search-0.3.1.tgz","fileCount":11,"integrity":"sha512-PIFl4qb0vIMBkkTWMFdfjFd3vLOWf2zMD9wMpQiSdw4LUbw6X8HEC+zFpJ9chTwDs29ULN6ko/orYoXPuodJVg==","signatures":[{"sig":"MEYCIQD0jKpoyYhMQapZwapUWYNBxrOh3cyk44lcTSU1hgoPXQIhALLFBum4XNBlPoiZ4tQMNKbfkC5hWyhOibYFZV+bDDoR","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":60358},"main":"lib/index.js","type":"module","engines":{"node":">=22"},"exports":{".":{"default":"./lib/index.js"},"./client":{"default":"./lib/client.js"},"./package.json":"./package.json"},"gitHead":"984b74ec2b52e96b618cab54eee2a03c806a6b6d","_npmUser":{"name":"ai4gensteam","email":"hello.junjie.duan@gmail.com"},"repository":{"url":"git+https://github.com/genesis-agents/dsh-plugins.git","type":"git","directory":"dsh-web-search-serper"},"_npmVersion":"11.6.2","description":"Web search for the DeepSeek Harness ctx.web seam, over interchangeable backends: Serper, Tavily, and Brave","directories":{},"_nodeVersion":"24.12.0","dependencies":{"@deepseek-ai/schemastery":"^3.18.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/dsh-web-search_0.3.1_1787520002607_0.3111138630351282","host":"s3://npm-registry-packages-npm-production"}},"0.3.2":{"name":"@ai4gensteam/dsh-web-search","version":"0.3.2","description":"Web search for the DeepSeek Harness ctx.web seam, over interchangeable backends: Serper, Tavily, and Brave","keywords":["dsh","dsh-plugin","deepseek-harness","web-search","serper","tavily","brave-search"],"homepage":"https://github.com/genesis-agents/dsh-plugins/tree/main/dsh-web-search-serper#readme","repository":{"type":"git","url":"git+https://github.com/genesis-agents/dsh-plugins.git","directory":"dsh-web-search-serper"},"bugs":{"url":"https://github.com/genesis-agents/dsh-plugins/issues"},"license":"MIT","type":"module","main":"lib/index.js","exports":{".":{"default":"./lib/index.js"},"./package.json":"./package.json","./client":{"default":"./lib/client.js"}},"engines":{"node":">=22"},"publishConfig":{"access":"public"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-runtime"],"platform":"web"}},"dependencies":{"@deepseek-ai/schemastery":"^3.18.1"},"gitHead":"fdaa3ba60cec7c4ec10d7145d65b1d8f2b85246e","_id":"@ai4gensteam/dsh-web-search@0.3.2","_nodeVersion":"24.12.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-Pb8qMDf9szsduOPuEdOkZEl7ATvtHpljUsM3FiTep5crQ8xpmkFNFfLgupxXyXIGxOSEdhgjdOV8K2zehdKOLg==","shasum":"50bdbb81a2d7f49df8a56bb33cd3f07dc952e819","tarball":"https://registry.npmjs.org/@ai4gensteam/dsh-web-search/-/dsh-web-search-0.3.2.tgz","fileCount":11,"unpackedSize":60046,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDfLWyq+SuEiIpBnTypwn0jeE2sgnzna4J1CoiEl8ETbQIhAJHscQwgeN78CQNN/3wBZVamMLWcnkZxLKzcWbbwfIvJ"}]},"_npmUser":{"name":"ai4gensteam","email":"hello.junjie.duan@gmail.com"},"directories":{},"maintainers":[{"name":"ai4gensteam","email":"hello.junjie.duan@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dsh-web-search_0.3.2_1787520704924_0.7796543646332725"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-23T20:39:51.086Z","modified":"2026-08-23T21:31:45.203Z","0.2.0":"2026-08-23T20:39:51.320Z","0.3.0":"2026-08-23T21:05:01.789Z","0.3.1":"2026-08-23T21:20:02.749Z","0.3.2":"2026-08-23T21:31:45.057Z"},"bugs":{"url":"https://github.com/genesis-agents/dsh-plugins/issues"},"license":"MIT","homepage":"https://github.com/genesis-agents/dsh-plugins/tree/main/dsh-web-search-serper#readme","keywords":["dsh","dsh-plugin","deepseek-harness","web-search","serper","tavily","brave-search"],"repository":{"type":"git","url":"git+https://github.com/genesis-agents/dsh-plugins.git","directory":"dsh-web-search-serper"},"description":"Web search for the DeepSeek Harness ctx.web seam, over interchangeable backends: Serper, Tavily, and Brave","maintainers":[{"name":"ai4gensteam","email":"hello.junjie.duan@gmail.com"}],"readme":"# dsh-web-search\n\nWeb search for the DeepSeek Harness web capability seam (`ctx.web`), over\ninterchangeable backends: **Serper**, **Tavily**, and **Brave Search**.\n\nThe directory is still called `dsh-web-search-serper`, from when Serper was the\nonly backend. The package is not: a name promising one provider while routing\nto three is a name that lies to whoever reads their dependency list.\n\n## Install\n\nTwo steps, and the second one is easy to miss.\n\n```sh\ndsh plugin --profile web add @ai4gensteam/dsh-web-search\n```\n\nThat is `pnpm add` in your profile directory — it installs the plugin and\n**does not enable it**. Nothing happens until it is in the bundle list in\n`~/.dsh/profiles/web/package.json`:\n\n```json\n{\n  \"dsh\": {\n    \"profile\": {\n      \"bundles\": [\n        \"@deepseek-ai/dsh-base\",\n        \"@deepseek-ai/dsh-web-app\",\n        \"@ai4gensteam/dsh-web-search\"\n      ]\n    }\n  }\n}\n```\n\nThen start the harness and open **Settings → 搜索 / Search** to paste a key.\nWithout one it registers, reports itself unconfigured, and every search fails\nwith a message saying which key is missing — which is the honest behaviour, but\nit is not search.\n\nAny one backend is enough. Serper and Brave both have free tiers; Tavily is\nincluded because its answers carry short summaries some models use well.\n\nThis is a **third-party plugin**. It imports nothing from the harness — every\ncapability it needs is reached through `ctx` at runtime, which is what the seams\nactually require. A plugin resolved from outside the harness checkout cannot\nreach the harness's own packages, so an import would either fail outright or\npull in a second copy whose classes the harness does not recognise. The one\ndependency is a schema library, because the settings service needs a schema\nobject it can call — and it duck-types it, so a locally installed copy is fine.\n\n## Why it exists\n\nThe shipped roster selects `deepseek-official`, which resolves\n`DEEPSEEK_API_KEY` at each search. A deployment running another model has no\nsuch key, and then web search has no working provider at all — not a degraded\none, none.\n\n## One provider, several backends\n\n`ctx.web` selects exactly ONE search provider, by id, and reads that id once\nwhen the service is constructed. So several search methods cannot be built by\nregistering several providers: the seam would still use one, and changing which\nwould mean editing composition config and restarting.\n\nSo one provider registers and routes internally. The seam keeps its\nsingle-provider model, and switching backends becomes a settings write that\ntakes effect on the next search.\n\nThis is also why the settings page shows a **choice** rather than a row of\ntoggles. The reference's tool page can enable many at once because its model\npicks a tool per call; showing switches that cannot all be on would\nmisrepresent what this seam does.\n\n## Configuring it\n\nSettings → **网页搜索 / Web search**. Each backend has a key field, a state\npill, and a Test button that runs a real search through the same code path a\nmodel's search takes.\n\nIt brings its own page because Settings → Plugins renders a hand-picked set of\nfirst-party namespaces — thirteen are registered on this deployment and three\nget a card. `settings.section` is a `list` slot, so the page is added beside\nModels and the rest rather than shadowing any of them.\n\nKeys may also come from the environment: `SERPER_API_KEY`, `TAVILY_API_KEY`,\n`BRAVE_API_KEY`. Prefer that — a key in a config file is a key in a backup.\nThe key field is declared `role('secret')`, so it is stripped from every wire\ndescriptor; a GET reports only whether one is stored and where it comes from.\n\n## Adding a backend\n\nA backend is a plain object in `lib/backends.js`: an id, a label, its key's\nenvironment variable, how to build the request, and how to map the answer.\nEverything else — key resolution, error classification, the settings page —\nis shared, so a new one is a file's worth of code and one array entry.\n\n## Mapping\n\n| | sources from | `content` from |\n|---|---|---|\n| Serper | `organic[]` | `answerBox`, else `knowledgeGraph.description` |\n| Tavily | `results[]` | `answer` |\n| Brave | `web.results[]` | — (returns none) |\n\nAn entry with no URL is dropped; an entry with no snippet is **kept** — these\nservices always carry a title and a URL, and a titled link is still a usable\nsource. Inventing a snippet would lie; omitting the field does not.\n\n`publishedAt` carries whatever recency the service reports, which for Serper\nand Brave is a relative phrase (`2 days ago`) rather than a timestamp. It is\npassed through as the string it is.\n\n## Failures\n\nRedirects are refused rather than followed — the target would be chosen by\nwhoever answered, and the API key travels in the request. HTTP errors, network\nfailures and unparseable bodies surface with code `WEB_PROVIDER_ERROR`; an\naborted request surfaces as `WEB_ABORTED`, including an abort that fires while\nthe body is being read, because cancellation is not a provider error.\n\n## Known limitation\n\nThe error carries a `code`, but it is not the harness's own `WebError` class\n(see above). `core/tools` attaches `{name, code}` as structured error metadata\nonly for its own class, so a failure from here reaches the model as its message\nwithout that metadata. The seam does not catch or re-wrap provider errors, so\nnothing else is affected.\n","readmeFilename":"README.md"}