dsh-zed-provider
Manifest validUse your Zed AI subscription (Pro / Student) from DeepSeek Harness: registers the zed-agent route with Claude + GPT models, an editable Models-page card and a model chooser.
dsh-zed-provider
Run your Zed AI subscription (Zed Pro / Student — the monthly prompt credits)
inside DeepSeek Harness. The bundle registers one LLM provider route,
zed-agent, whose turns are minted and streamed through Zed Cloud
(https://cloud.zed.dev) exactly as the Zed editor does it.
Install
npm install -g dsh-zed-provider # or: pnpm add -g dsh-zed-provider
For a DeepSeek Harness profile the bundle is installed rather than imported, so the patch layer below is what registers the provider route:
- insert:
- id: llm-zed
name: "dsh-zed-provider"
config:
apiKeyEnv: ZED_CREDENTIALS
Installing from git also works: pnpm runs the prepare hook, which builds
lib/ for you.
Link your Zed account
Type /zed-login in the Harness composer. The command opens Zed's native-app
sign-in page, waits for the loopback callback, and stores the resulting grant in
the Harness credential store (llm-zed/zed-agent). Later requests reuse it, and
the short-lived gateway token is minted automatically and refreshed on demand.
Credentials are resolved in this order, so any one of them is enough:
config.credentials— a literal"<userId> <accessToken>"(or JSON) value.config.apiKeyEnv(defaultZED_CREDENTIALS) — resolved through the credential seam. The Models settings page shows this as the Zed card's key field, so a credential pasted there works without/zed-login.- The
llm-zed/zed-agentgrant record written by/zed-login. - The Zed editor's own local keyring entry, on macOS (
zed.dev) and Linux (secret-tool), imported once into the grant record — sign in inside Zed and this plugin picks it up by itself.
apiKeyEnv is spelled that way because the Models page reads exactly that field
name; its value is a credential reference, and what it holds is the
"<userId> <accessToken>" pair Zed issues.
Settings UI
Settings → Models → the Zed card carries this bundle's own editor
(lib/client.js, a client-module half registered on the page's
settings.models.provider-card seat for the llm-zed namespace):
- credential status, from the card's credential join plus a live
credentials/describe; - a credential field that stores
"<userId> <accessToken>"under the referenceapiKeyEnvnames (read live from the settings document, defaultZED_CREDENTIALS), with Remove to unset it; - a refusal instead of a silent no-op when that reference is supplied by a read-only source such as an environment variable.
It also carries the model chooser: the Host registers
llm.registerModelDiscovery for this namespace, the page reads it through
llm/discoverModels, and the tick boxes write visibleModels through
settings/mutate. That field is volatile, so a tick changes the picker on the
next catalog read without a reload; an empty or complete selection unsets it,
meaning "offer every model this plugin can run".
baseUrl stays a composition-only field in cordis.patch.yml.
The client fiber hard-injects only slots, locale and remote, and picks the
credential remote up through ctx.inject(['remote.credentials'], …). That is
deliberate: the web boot asserts that every client entry activated, so a root
client plugin that waits on a service the settings UI keeps private (such as
settingsScope) takes the whole GUI boot down with
<package>: pending (waiting for service: …).
Models
listModels reads GET /models with your credential and advertises the models
Zed reports for the backends this plugin implements — Claude (Anthropic
Messages) and GPT (OpenAI Responses). Zed's Gemini and Grok entries are
filtered out rather than offered broken; selecting one anyway fails fast with
UNSUPPORTED_CONTENT.
Each model carries Zed's own context window, output cap, and — when
supports_thinking — the effort levels off / low / high / max, mapped
onto Anthropic extended thinking budgets.
Configuration
| Field | Default | Meaning |
|---|---|---|
apiKeyEnv | ZED_CREDENTIALS | Credential reference holding "<userId> <accessToken>". |
credentials | absent | Literal credential value; highest priority, composition-time. |
baseUrl | https://cloud.zed.dev | Zed Cloud endpoint. |
visibleModels | all supported | Narrow the catalog to these model ids. |
Quota
Zed owns the accounting: no plan or no credits left answers HTTP 402, which
surfaces as a QUOTA failure naming https://zed.dev/account. The plugin does
not invent percentages or limits.
Development
pnpm install (which runs the prepare hook), then pnpm check —
typecheck, build, tests. Sources are TypeScript under src/; lib/ is build
output and is git-ignored, because the prepare/prepack hooks rebuild it
wherever the package is consumed and the published tarball carries the built
copy through the files allowlist.
A profile can install this bundle as a link: to a working copy, in which case
that copy's lib/ is what the Host imports: rebuild after changing src/, or
the Host keeps running the previous build.
node --test test/*.test.mjs covers the pieces that fail silently in
production: the NDJSON → StreamChunk translation for both backends
(src/frames.ts framing, src/parse.ts Anthropic events, src/responses.ts
Responses events), the RSA sign-in handshake (src/signin.ts), the request
projections (src/anthropic.ts, src/responses.ts), and the card's slot
registration, credential and model-selection wiring (src/client.ts, loaded
under a stubbed module loader in test/client.test.mjs). The tests import the
built lib/, because that is what the Harness actually runs. Only
src/index.ts touches the Harness runtime.
Two install-time facts worth knowing: the Host half is re-imported only by a
profile restart (ESM caches by URL), and the client half is composed from the
Loader's entries at Host boot, so adding or removing exports["./client"] takes
a restart too — after that it is served per request under a content hash, so
client-side edits reach the browser on the next load.
Releasing
.github/workflows/publish.yml publishes on a published GitHub release (or a
manual run), after typechecking, building and testing. It authenticates with
npm trusted publishing: the workflow asks for id-token: write, npm
exchanges that OIDC token for a short-lived publish credential, and the result
carries a provenance attestation tying the tarball to this repository and commit.
No npm token is stored in the repository, and the publish step upgrades npm to a
version that supports it (trusted publishing needs npm ≥ 11.5.1, newer than the
one bundled with Node 22).
The version comes from package.json and npm refuses to publish over an
existing version, so bump it in the commit the release points at. lib/ is
git-ignored and rebuilt by the prepack hook, so nothing built is ever
committed. The registry bootstrap for a brand-new package name still needs one
token-authenticated publish, because npm cannot attach a trusted publisher to a
name that has never been published; this package was bootstrapped that way
(0.0.0-stage, then 0.1.0), and every release from here goes through OIDC.
Comments
Loading…