krislavten / ai-sdk-provider-dsh

Listed

AI SDK provider that drives a DeepSeek Harness (dsh) runtime as a LanguageModelV3 — works on AI SDK v6 and v7

mainModel View source

Installation

npm install ai-sdk-provider-dsh

This command is generated from the GitHub repository address. Inspect the upstream README and source before running it; pin a release or commit when reproducibility matters.

README

Maintainer-authored documentation snapshot.

View on GitHub ↗
Commit d10d96fSynced Aug 18, 2026

ai-sdk-provider-dsh

AI SDK provider that drives a DeepSeek Harness (dsh) runtime as a language model.

dsh is a full agent harness (agent loop, tools, skills, MCP, sessions) from DeepSeek AI. This provider wraps a dsh runtime subprocess behind the AI SDK LanguageModel interface, so you can drive a harness agent from AI SDK generateText / streamText the same way ai-sdk-provider-claude-code drives Claude Code — while keeping the AI SDK as the single orchestration surface.

Version Compatibility

This provider implements the LanguageModelV3 specification (specificationVersion: 'v3'), the interface shared across AI SDK majors. A single build serves both:

AI SDK@ai-sdk/providerStatus
ai@^6@ai-sdk/provider@^3✅ supported
ai@^7@ai-sdk/provider@^4✅ supported (V3 models are first-class in v7)
RequirementValue
Node.js>=22.19
Module formatESM only
DeepSeek Harness familypinned exact 0.1.0-rc.6

Upstream status: dsh is in developer preview (0.1.0-rc.x); DeepSeek documents breaking changes as their release policy. This provider pins the harness SDK family to exact versions, so the runtime version is a deliberate platform-side decision — upgrade the pin explicitly, never by range drift.

Install

npm install ai-sdk-provider-dsh

The dsh runtime is bundled: the package ships a default runtime composition (runtime/cordis.yml) plus the dsh-jsonrpc-agent bin (via @deepseek-ai/dsh-sdk-jsonrpc-demo), and all runtime plugins are pinned exact versions in dependencies. A provider instance spawns a working runtime out of the box — no separate install.

Credentials come from the runtime's environment:

export DEEPSEEK_API_KEY=sk-...                                        # required
export DEEPSEEK_BASE_URL=https://api.deepseek.com                     # optional; any OpenAI-compatible gateway works

Quick Start

streamText (AI SDK v7)

import { streamText } from "ai";
import { createDsh } from "ai-sdk-provider-dsh";

const dsh = createDsh({
  runtime: { provider: "deepseek-official", model: "deepseek-v4-flash" },
});

const result = streamText({
  model: dsh.languageModel("deepseek-v4-flash"),
  instructions: "You are a coding agent.",
  prompt: "run the tests",
});

const text = await result.text;
console.log(text);

streamText (AI SDK v6)

import { streamText } from "ai";
import { createDsh } from "ai-sdk-provider-dsh";

const dsh = createDsh({ runtime: { provider: "deepseek-official", model: "deepseek-v4-flash" } });

const result = streamText({
  model: dsh.languageModel("deepseek-v4-flash"),
  system: "You are a coding agent.", // v6 name; v7 uses `instructions`
  prompt: "run the tests",
});

generateText

import { generateText } from "ai";
import { createDsh } from "ai-sdk-provider-dsh";

const dsh = createDsh({ runtime: { provider: "deepseek-official", model: "deepseek-v4-flash" } });
const { text } = await generateText({
  model: dsh.languageModel("deepseek-v4-flash"),
  prompt: "say hello",
});

Provider factory

const dsh = createDsh(options);        // returns the provider
dsh.languageModel("deepseek-v4-flash") // the LanguageModel
dsh("deepseek-v4-flash")               // callable alias (AI SDK provider convention)
await dsh.close();                     // tear down the runtime subprocess (idempotent)

Runtime Options

OptionDefaultMeaning
providerrequiredmodel provider route passed to the runtime handshake (deepseek-official, or a pi-ai catalog route)
modelrequiredmodel id passed to the runtime handshake
envinherits process.envenvironment for the runtime subprocess: credentials (DEEPSEEK_API_KEY), DEEPSEEK_BASE_URL, DSH_CWD, DSH_SESSION_ROOT, …
cwdprocess.cwd()subprocess working directory
configPathbundled runtime/cordis.ymla different cordis.yml composition
binPathbundled dsh-jsonrpc-agenta different runtime bin
command / argsnode + [bin, config]full custom launch vector (set both together)
maxTokenspositive output-token cap per root-agent request
requestTimeoutMsSDK defaultper-request timeout for the JSON-RPC transport
disposeEofGraceMs / disposeGraceMsSDK defaultssubprocess teardown ladders (EOF → SIGTERM → SIGKILL)
sessionIdfresh UUIDfixed session id; keep it to continue one harness session across turns

The bundled runtime

The default composition (runtime/cordis.yml) exposes:

  • bash (foreground), read/write/edit (fs), subagent, todo_write — tools execute inside the harness
  • JSONL session persistence with automatic context compaction
  • $DSH_SYSTEM_PROMPT selects the deployment persona

For environments that cannot build node-pty (no Linux prebuild — e.g. minimal containers, WSL without libc6-dev), use the no-pty composition:

runtime: {
  provider: "deepseek-official",
  model: "deepseek-v4-flash",
  configPath: require.resolve("ai-sdk-provider-dsh/runtime/cordis.minimal.yml"),
}

How it works

  • Each provider instance spawns (lazily) one dsh runtime subprocess speaking stdio JSON-RPC (the dsh SDK protocol).
  • doGenerate / doStream translate AI SDK LanguageModelV3CallOptions into a dsh prompt, then map the runtime's session.event stream back into AI SDK stream parts (text-start/delta/end, reasoning-start/delta/end, tool-input-start/delta/end, tool-call, finish).
  • Tools execute inside the harness — the provider is a thin pass-through (like ai-sdk-provider-claude-code): tool calls surface as providerExecuted: true parts and the AI SDK never re-executes them.
  • Multi-turn sessions: one provider instance keeps one runtime subprocess; with a fixed sessionId, follow-up turns continue the same harness session (the runtime persists the session log). Verified end-to-end: turn 1 stores a secret code, turn 2 recalls it.
  • Abort: an aborted call surfaces the original abort reason (never a wrapped transport error); pre-aborted signals throw immediately; the abort listener is removed on completion.

Provider Metadata

Each response exposes dsh metadata under providerMetadata['dsh'] (AI SDK v7: result.finalStep.providerMetadata, or await stream.finalStep for streamText; v6: result.providerMetadata):

FieldTypeMeaning
sessionIdstringthe harness session id this call ran on
turnIdnumber?last observed turn number
terminalReasonstring?final turn end kind when not completed (aborted, error, max-tokens, blocked, interrupted)

Error Diagnostics

Errors from the runtime boundary are classified into AI SDK APICallErrors. A sanitized stderr tail is appended to the message so CLI failures are visible in logs:

dsh runtime subprocess failed: runtime exited | stderr (tail): ...; ...
import { generateText } from "ai";
import { createDsh, getErrorMetadata, isAPICallError } from "ai-sdk-provider-dsh";

try {
  await generateText({ model: dsh.languageModel("deepseek-v4-flash"), prompt: "Hello!" });
} catch (error) {
  if (isAPICallError(error)) {
    console.error(getErrorMetadata(error)?.stderr);
    console.error("retryable:", error.isRetryable);
  }
}

Classification map:

Runtime failureAI SDK errorRetryable
TransportClosedError (subprocess died / stdio closed)APICallError
RequestTimeoutErrorAPICallError
SdkProtocolError (wire violation)APICallError
JsonRpcResponseError (runtime rejected request)APICallError
Node spawn failure (ENOENT bin, …)APICallErroronly EAGAIN/EMFILE
Missing/invalid API keyLoadAPIKeyError (via createAuthenticationError)

Limitations

  • Requires Node.js >=22.19; ESM only.
  • No mid-turn cancel on the SDK wire: aborting a turn rejects the current call; the runtime subprocess and session log remain for follow-up turns. dsh.close() tears the subprocess down (EOF → SIGTERM → SIGKILL).
  • Skills use the dsh native mechanism (SKILL.md bundles discovered from .dsh/skills, .agents/skills, $DSH_HOME/skills) — the reskill skills.json/skills.lock convention is not applied by this provider.
  • Tool execution is harness-internal: AI SDK tools / toolChoice are not executed by the AI SDK; configure tools through the runtime composition (cordis.yml) or $DSH_* env.
  • Some AI SDK call options are accepted but not forwarded to the harness: temperature, topP, topK, stopSequences, seed — the harness owns sampling.
  • dsh is in developer preview; DeepSeek documents breaking changes as release policy. Pin the provider version and the harness family (0.1.0-rc.6) deliberately.
  • The bundled default runtime needs node-pty on Linux (compiled at install; no prebuild). Use cordis.minimal.yml (no bash) where that is unavailable.

Development

pnpm install
pnpm run check    # typecheck
pnpm run test     # unit tests (fake runtime) + e2e (real runtime, keyless replay)
pnpm run lint     # biome
pnpm run build    # tsup → dist/

Tests never need a real API key: unit tests drive a fake runtime with synthetic event streams (the ai-sdk-provider-claude-code philosophy), and e2e tests boot the real dsh runtime against recorded session fixtures replayed by @deepseek-ai/dsh-llm-replay.

Recording new fixtures (requires a live key)

DEEPSEEK_API_KEY=sk-... node scripts/record-fixture.mjs   # writes tests/fixtures/*.jsonl

License

MIT

Project files and signals

Shown items are public repository signals detected in the directory snapshot.

TestsDetected
ExamplesDetected

Repository information

Language
TypeScript
License
MIT
Latest release
v0.2.0
Last updated
Aug 13, 2026, 4:14 PM

Install deliberately

Review source code, permissions, lifecycle hooks, dependencies and network access. Test untrusted plugins in an isolated environment.