Changelog
September 14, 2026 · View on GitHub
[Unreleased]
[0.2.1] - 2026-09-14
Changed
- The runtime platform matrix is documented as a pointer to the upstream published wheel list rather than a fixed enumeration, so the targets a consumer is told about stay in step with what upstream actually publishes (Windows x64 included; macOS needs the sibling
-spawn-helper).
Fixed
RunResult::final_responsenow falls back to an earlier rootassistant/messagewhen the last one is malformed (non-objectdataor non-arraycontent), matching the Python SDK'sfinal_responsereversed-scan algorithm. The previous implementation returned""instead of trying an earlier event.- Corrected the public rustdoc on
RunResult::final_responseand the normative spec §6.2, which both incorrectly claimedfinal_response"never falls back to an earlier event" while citing the Python source that does fall back. The behavior is unreachable from a conformant runtime (which always emitsmessage.contentas a JSON array) but the algorithm and docs now agree. RunResult::final_responsetreats a non-stringtextblock (includingnull) as"". That is a recorded divergence from the Python SDK, which coerces a truthy non-string viastr()(42→"42",true→"True"). There is no runtime behavior change.- A
HarnessClientdropped without an explicitclose()now closes its notification channel, so a subscription awaiting the next notification returnsError::TransportClosedpromptly instead of only when the aborted background reader task happens to be torn down. That error reports the closed reason, plus the runtime's exit code and captured stderr tail when they were observed. Config::dsh_binis the current runtime-binary override name (renamed fromConfig::runtime_bin; theDSH_RUNTIME_BINenvironment route is preserved).- Runtime acquisition now documents all five targets upstream publishes for the runtime — Linux x64, Linux arm64, macOS arm64, macOS x64, and Windows x64 — and no longer states that macOS x64 has no wheel. A macOS x64 wheel is published, so Route B (the self-contained wheel) works there; Route C (build from source) remains the route that reproduces the exact contract basis this crate was verified against, and the fallback for any platform with no published wheel.
Session::runno longer wedges indefinitely when the runtime dies mid-turn (stdout EOF aftersession/promptsucceeds but before the inbox receipt or root-idle notification arrives). The high-level API surfacesError::TransportClosedwith the closed reason and the stderr tail the end-of-stream path captured, plus the exit code when the best-effort poll of the exiting child observed it, as its doc contract always promised ("once the channel (or the client) is closed,Error::TransportClosedis returned").NotificationSubscription::recvparked inbroadcast::Receiver::recv().awaitnow wakes withTransportClosedwhen the runtime dies spontaneously (stdout EOF without an explicitclose), instead of hanging forever. The fix shares the original broadcastSenderbetweenHarnessClientand the read loop and drops it on the read loop's EOF path, so the channel closes and a parkedrecvresolves withRecvError::Closed. Subscriptions created after runtime death remain born-failed.
[0.2.0] - 2026-09-09
Added
- CI now enforces the changelog-fragment discipline: every PR changing user-visible behavior must add or modify a
.changes/unreleased/fragment in the same PR; Release-prep output is exempt (recognized by arelease/*head branch or a diff confined to the paths Release prep writes). AGENTS.mddocuments the fragment rule: one fragment per user-visible change (frontmattercategory:+ English bullets),CHANGELOG.mdis machine-assembled and never hand-edited.- New
Configfieldsprofile(default"sdk"),patches, anddsh_home. Config::reasoning_effort— sent oninitializeas the wire keyreasoningEffortonly when set to a non-empty string, and omitted entirely when unset or blank.Session::runaccepts anon_notificationcallback that observes every notification delivered to the session-tree subscription, in wire order.ContentBlock::File— the sixth typed block variant,FileAttachmentRef {attachmentId, name, bytes}.ImageAttachmentRef::original_dimensions— carried on the wire asoriginalDimensionsso a parsed image block round-trips without loss.DeepSeekHarness::dsh_home— reads back the resolved absolute home (the same valueConfig::resolve_dsh_homecomputes).
Changed
- The npm-published
dshCLI (@deepseek-ai/dsh) is now documented as a runtime-acquisition route: install it withnpm install -g @deepseek-ai/dshand pointDSH_RUNTIME_BINat it; the bin is a Node.js script, so Node.js must be onPATH. - The
RuntimeNotFoundhint now names the npm route alongside the bring-your-own and build-from-source routes. - README Runtime acquisition now documents three runtime routes — the npm CLI (
@deepseek-ai/dsh, recommended), the platform wheel (self-contained, no Node.js), and build from source — and states that the interactivedshCLI (@deepseek-ai/dsh) is the SDK runtime this crate spawns as a subprocess (dsh --profile sdk) and speaks to over stdio JSON-RPC. - The npm-published
dshCLI is now the recommended runtime-acquisition route: install it withnpm install -g @deepseek-ai/dshand pointDSH_RUNTIME_BINat it (the bin is a Node.js script, so Node.js must be onPATH). - The platform wheel is now documented after the npm CLI as the self-contained fallback (no system Node.js at runtime), with the published-target matrix corrected to Linux x64, Linux arm64, macOS arm64, and Windows x64 — macOS x64 is not published, so that platform uses the build-from-source route.
- Building from source is documented as the only route that reproduces the exact contract basis this crate was verified against.
- README Runtime acquisition (Route B) documents the runtime wheel's ripgrep
-rgsidecar: copy it along when relocating the executable. - The runtime is launched as
dsh --profile <name>with ordered--patch <path>overlays, not a bare program spawn. DSH_HOMEnow resolves by upstream precedence —Config::dsh_home, then a non-emptyDSH_HOMEenvironment variable, then~/.dsh(blank counts as unset). Unlike the Python SDK, the crate falls back to~/.dshinstead of raisingValueError; the resolved absolute home is observable viaConfig::resolve_dsh_home.- The
initializehandshake is bounded byConfig::initialize_timeout(default 30 s;Noneis a deliberate opt-out). Error::RequestTimeoutgained aprofile: Option<String>field naming the selected DSH profile when the handshake had one, so a wedgedinitializeis diagnosable.
Removed
Config::session_rootis removed; useConfig::dsh_home— sessions live under the resolved$DSH_HOME/sessionsinstead.Config::cordis_configand theDSH_CORDIS_CONFIGenvironment injection are removed; use the profile tree (Config::profile+Config::patches) — no config file is passed to the runtime.- The
DSH_SESSION_ROOTandDSH_CWDenvironment injections are removed; sessions live under the resolvedDSH_HOME, and the workspace directory reaches the runtime asConfig::cwd(sent asinitialize.cwd). RunResult::session_rootis dropped with no replacement — upstream removed it and asserts its absence.Config::launch_args_overrideis removed; useConfig::dsh_bin+Config::profile+Config::patches— the launch is composed from typed fields, not an opaque argv.Config::runtime_binis renamed toConfig::dsh_bin(theDSH_RUNTIME_BINenvironment override is preserved).- The bundled
assets/cordis.ymlis removed; use the profile bundle shipped with the runtime instead. - The
assets/cordis.ymlentry inCargo.toml [package] includeis removed; the file no longer exists, and the profile bundle replaces the deleted asset. DEFAULT_CORDIS_YMLandbundled_default_config_pathare removed; use the profile bundle instead.
[0.1.0] - 2026-08-17
Changed
- README install instructions no longer pin a point version: the documented command is a bare
cargo add deepseek-harness-sdkwith a"*"dependency line, leaving version selection to the user; the pre-release note keeps only channel-level guidance (explicit@0.1.0-alphastyle request while on a pre-release line).
[0.1.0-alpha.2] - 2026-08-17
Added
- GitHub Workflow release pipeline: dispatch Release prep → review one
release v<version>PR → merging publishes (annotated tag → verify → GitHub Release → crates.io via Trusted Publishing; no registry token, no localcargo publish). - Fragment-driven changelog: user-visible changes accumulate as
.changes/unreleased/fragments and are assembled intoCHANGELOG.mdby the Release prep run. - Backfilled
0.1.0-alpha.1history:CHANGELOG.mdsection, git tagv0.1.0-alpha.1, and a GitHub Release for the published crate.
Fixed
- Release prep now opens a new release PR when a previous PR for the same version was already merged (re-prep after a rollback), instead of failing on
gh pr reopen.
[0.1.0-alpha.1] - 2026-08-16
Added
- Initial release:
deepseek-harness-sdkis a pure-client Rust SDK that spawns the official DeepSeek Harness (DSH) runtime as a subprocess and speaks its stdio JSON-RPC 2.0 protocol. - Protocol types with serde 1:1 wire mapping and a merge-extensible
ContentBlockwith unknown-content passthrough; line-framed stdio transport with skip-malformed-lines parity and an oversized-frame guard. - Low-level
HarnessClient: request dispatch with timeout abandonment, session-tree tracking fromsubagent.startededges, broadcast notifications, and an unconditional close ladder. - High-level Python-parity API:
DeepSeekHarness::start,Session::run, andRunResultwithfinish_reasonandsession_root. - Typed errors across the API surface:
Error::SdkProtocol,Error::JsonRpc(preservingcodeanddata), andError::RuntimeNotFound. - Published to crates.io as
0.1.0-alpha.1— install withcargo add deepseek-harness-sdk@0.1.0-alpha.1(pre-release versions require an explicit version; a barecargo adddoes not resolve to a pre-release). - Bilingual installation-first READMEs (English / 中文) covering the quickstart, runtime acquisition routes, and platform support.
- Bring-your-own runtime (Plan A): the binary is resolved from
Config::launch_args_override(full argv, verbatim),Config::runtime_bin, or theDSH_RUNTIME_BINenvironment variable — the crate never downloads, bundles, or ships a runtime. - Bundled default
cordis.ymlinjection (byte-identical to the official default) when noDSH_CORDIS_CONFIGis set, plus environment injection ofDSH_CWD,DSH_SESSION_ROOT, and model credentials. - A missing runtime fails fast with
Error::RuntimeNotFound, whose message names the acquisition routes and points to https://github.com/deepseek-ai/deepseek-harness.
Compatibility
RunResultfollows the Python SDK field set, includingfinish_reasonandsession_root— fields the TypeScript SDK'sRunResultlacks.- The wire protocol is pre-release: the runtime identifies as
serverInfo0.0.1 with a strict name check and no version negotiation. - Consumes runtime builds for linux-x64, linux-arm64, and macos-arm64; there is no Windows support (upstream ships no Windows runtime builds).
- No mid-turn cancel or session-close RPC:
Session::runwaits until the root session reportsidle.