YePpHa/dsh-web-search-kagi

dsh-web-search-kagi

Kagi Search v1 provider for the DeepSeek Harness.

미분류deepseekdeepseek-harnesskagi

dsh-web-search-kagi

Kagi Search v1 provider for the DeepSeek Harness web capability seam (ctx.web).

Published on npm as dsh-web-search-kagi.

Registers a WebSearchProvider with id kagi that POSTs to Kagi's https://kagi.com/api/v1/search with bearer auth and maps data.search[] onto the seam's portable citation shape (url, title, snippet, publishedAt).

Compatibility

DSH 0.2.0-rc.1 or newer within the 0.2 release line. Before a profile imports a plugin or bundle, DSH matches every @deepseek-ai/dsh and @deepseek-ai/dsh-* range in its peerDependencies against the running DSH version itself, so those peers are declared as a range (>=0.2.0-rc.1 <0.3.0-0) and not as an exact version. An exact pin makes the package unloadable on the next DSH release: 0.3.1 pinned 0.2.0-rc.1 and DSH 0.2.0-rc.2 refused it, even though dsh-web, dsh-credentials, and dsh-settings are byte-identical in both releases.

Install

Add the package to the profile that runs the web app:

dsh plugin --profile web add dsh-web-search-kagi

This writes dsh-web-search-kagi to the profile's package.json and installs it. Without the dsh CLI, run pnpm add dsh-web-search-kagi from the profile directory instead.

Then compose it in $DSH_HOME/profiles/web/cordis.patch.yml:

- id: web
  config:
    searchProvider: kagi

- id: web-search-deepseek
  disabled: true

Restart the harness.

Bundle layer

The package declares dsh.bundle.patch in package.json and ships cordis.patch.yml at its root: it is a dsh bundle, not just a plugin package. dsh plugin --profile web add dsh-web-search-kagi therefore also appends the package to dsh.profile.bundles, and that layer inserts the web-search-kagi provider row, so the profile patch above needs no insert entry of its own.

Which provider the web seam serves is deployment configuration and stays in the profile patch above: bundle layers are applied before it, so its searchProvider: kagi wins over both the bundle row and the base layer's searchProvider: deepseek-official.

Upgrading from 0.2.x: delete the - insert: block from the profile patch. The bundle contributes web-search-kagi now, and two loader entries sharing one id fail the boot with duplicate loader entry id: web-search-kagi. The web and web-search-deepseek patches stay.

API key

The provider reads the key from the credentials service under the KAGI_SEARCH_API_KEY reference. Put it in $DSH_HOME/.credentials.yaml:

KAGI_SEARCH_API_KEY: your-kagi-api-key

You can also set it later in the web UI at Settings → Plugins → Plugin configuration, whose API key field writes the same credential. Get a key from Kagi.

Config

FieldDefaultNotes
baseURLhttps://kagi.com/api/v1Kagi Search API v1 base.
limit10Fallback result limit, 1 to 1024.
safeSearchtrueRequests Kagi safe_search.
apiKeyunsetWrite-only; moved into the credentials service (see below).

Every field is marked .volatile(), which is what makes it editable in the settings page without restarting the plugin.

Settings UI

The plugin's settings show in the web UI at Settings → Plugins → Plugin configuration. No cordis.patch.yml edit is needed to change these values after install, and the package ships no browser bundle: DeepSeek Harness generates the page from the plugin's Config schema.

Every write carries the revision it read, and overridden fields show a Reset to default button. The layering is schema defaults, then the composition entry config, then the user document, so the config: block in cordis.patch.yml stays the deployment's base layer.

The API key field

apiKey is a role('secret') field, so the settings wire redacts it. It is not configuration: when the form supplies a value the plugin writes it to the credentials service under KAGI_SEARCH_API_KEY and clears the field, so later settings writes do not restate it.

Prefer $DSH_HOME/.credentials.yaml if you want the secret to never pass through a settings form. That path writes the value nowhere but the credentials store; the form has to accept the value before the plugin can redirect it, so a key typed into the page does reach the profile patch and the settings document when the form writes them.

The provider re-resolves the credential on every operation, so a key changed in either place reaches the next search with no restart.