Skip to content

feat: support DeepSeek's official Harness desktop app and dsh 0.1.7 profiles - #248

Merged
yujiezhang-ops merged 9 commits into
mainfrom
codex/deepseek-harness-desktop
Sep 26, 2026
Merged

yujiezhang-ops merged 9 commits into
mainfrom
codex/deepseek-harness-desktop

Conversation

@yujiezhang-ops

@yujiezhang-ops yujiezhang-ops commented Sep 26, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Stacked on #247 (codex/dsh-latest-adaptation); the first two commits here are that PR's. Review the last two commits, or merge #247 first and this diff collapses to them.

DeepSeek now ships its own desktop application, DeepSeek Harness (com.deepseek.dsh, Developer ID team NAN929V4UM, served from download.deepseek.com). The dsh-desktop Agent installed a third-party build (anywhere-labs "DSH Desktop") and wrote configuration into ~/.dsh/settings.yaml, which dsh 0.1.7 no longer reads.

Configuration layer (P0)

  • dsh 0.1.7 moved live configuration to $DSH_HOME/profiles/<profile>/cordis.patch.yml, a top-level YAML sequence of loader patch rows (whole-config replacement per row). The legacy settings.yaml is imported once on first start and renamed settings.yaml.imported; agent-default-model has no import mapping, so a legacy write after that loses the default selection.
  • New ResolveDSHConfigPath(home, profile). The desktop Agent (desktop profile) always writes the patch and creates the directory ahead of the app's first launch: the Electron shell is 0.1.7-only, and its initProfile fills in package.json / pnpm-workspace.yaml only where absent. The CLI Agent (web profile) writes the patch once dsh has created that profile, otherwise the legacy file, since npm latest is still 0.1.5.
  • Patch writes keep every other provider in the llm-pi-ai row; rows the write does not address come back byte-for-byte (two-space indent, !!js tags and quoting preserved). Credentials stay at $DSH_HOME/.credentials.yaml from either layout.
  • ReadDSHConfig tells the layouts apart by the document's root node kind.
  • Fixture with the account-platform records the desktop app keeps beside refs proves the credential merge leaves them intact.

Desktop entry (P1)

  • Package resolved from the vendor's electron-updater feed (dsh-desk/feeds/mac-arm64/nightly-mac.yml, dsh-desk/feeds/win-x64/nightly.yml), served bytes verified against the feed's sha512. Host allowlist reduced to download.deepseek.com; GitHub release lookup and dshdesktop.cn mirror removed.
  • Signature pinned to the vendor: bundle id + Team ID + spctl notarization on macOS, publisher on Windows. Previous checks accepted any valid signature.
  • Only mac-arm64 and win-x64 are published. One function decides both Supported and the feed: Intel macOS is unsupported (no install entry shown), Windows on ARM installs the x64 build. Detection matches on bundle identifier and reports the version from disk (the app self-updates).
  • Unofficial mark and disclaimer dropped for this row; the disclaimer path is still tested on a synthetic entry. Agent id dsh-desktop is kept so existing bindings resolve.

Test plan

  • go test ./..., go test -race on internal/config, internal/desktopapp, internal/app
  • go vet ./..., staticcheck ./...
  • python3 scripts/check-docs.py
  • cd frontend && pnpm run test && pnpm run build
  • Verified against the real release on this machine: codesign -dv identity, spctl --assess accepted, feed sha512 matches the served mac-arm64 zip byte-for-byte, ~/.dsh/profiles/desktop/cordis.patch.yml is where 0.1.7 imported the old settings.

Review follow-ups (addressed in db3eacd, 3a70a65)

  • P1: desktop Agent no longer falls back to settings.yaml when profiles/desktop/ is absent.
  • P2: Supported now agrees with the feed lookup (Intel Mac unsupported; Windows ARM supported via x64).
  • P2: WriteDSHOfficial validates reasoningEffort before touching .credentials.yaml.
  • P3: scaffold header comment preserved; two-space indent; first-row-per-id reader.

Windows verification (ea8ad96, c93ff4e)

On Windows 11 x64 against the real deepseek-harness-0.1.7-rc.2-win-x64.exe:

  • feed sha512 and size match the served bytes.
  • Get-AuthenticodeSignature: Valid, subject CN="Hangzhou DeepSeek Artificial Intelligence Co., Ltd.", O="Hangzhou DeepSeek Artificial Intelligence Co., Ltd.", …, issuer GlobalSign GCC R45 EV CodeSigning CA 2020.
  • The previous check refused this genuine installer twice over: the pinned name lacked the trailing period, and the PowerShell O= extraction split the quoted value at its comma ("Hangzhou DeepSeek Artificial Intelligence Co.). O= is now read in Go with a quote-aware parser (unit-tested), and verifyDSHWindowsInstaller accepts the real .exe end to end while verifyZCodeWindowsInstaller still refuses it.
  • The installer is an assisted NSIS wizard, so installer-started hands off to the user rather than completing on its own.
  • Installed through the wizard with defaults: per-user, %LOCALAPPDATA%\Programs\DeepSeek Harness\DeepSeek Harness.exe (HKCU uninstall entry, /currentuser), which is exactly what dshWindowsCandidates probes; Inspect reports it installed.
  • First launch creates ~/.dsh/profiles/desktop/cordis.patch.yml as the header comment followed by a flow-style [], and ~/.dsh/.credentials.yaml with version: 1 and records but no refs. Against a copy of that directory WriteDSH refused (credentials refs must be an object); c93ff4e creates refs when absent and writes the empty list back in block style so rows are not all emitted on one line. Covered by TestWriteDSHProfileIntoAFreshDesktopInstall, and re-run against the real copy.
  • go test ./internal/... on Windows: no new failures relative to main; the remaining ones are pre-existing Unix-mode / macOS-path assertions.

Not verified here

  • Windows Inspect reports no version (macOS reads it from the bundle); the exe's ProductVersion or the HKCU DisplayVersion would supply it. Follow-up.
  • Whether the running app accepts a BootAgent-written profile end to end (written and checked against a copy of the real ~/.dsh, not loaded by the live app).
  • Whether the desktop welcome page's "API key configured" check recognises the BOOTAGENT_API_KEY reference (gateway providers). Follow-up.

🤖 Generated with Claude Code

yujiezhang-ops and others added 4 commits September 17, 2026 09:51
DeepSeek Harness 0.1.7 removed $DSH_HOME/settings.yaml. Live configuration
now lives in $DSH_HOME/profiles/<profile>/cordis.patch.yml, a top-level
sequence of loader patch rows where each row replaces one plugin entry's
whole config. dsh imports the legacy file once on first start and renames it
settings.yaml.imported; agent-default-model has no import mapping, so a write
into the legacy file after that loses the default selection.

Resolve the target per profile: the patch when its profile directory exists,
the legacy file otherwise, so the npm `latest` CLI (0.1.5) keeps working
while the desktop app and `next` (0.1.7) read what BootAgent writes. The CLI
Agent uses the `web` profile, the desktop Agent `desktop`. Writes into the
patch keep every other provider in the llm-pi-ai row and round-trip the
bundle's `!!js` tagged scalars untouched; the credential store stays at the
harness home and is located from either layout.

ReadDSHConfig tells the two layouts apart by the document's root kind. A
fixture with the account-platform `records` the desktop app keeps beside
`refs` proves the credential merge leaves them intact.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…uild

The dsh-desktop entry installed anywhere-labs' "DSH Desktop". DeepSeek now
publishes its own desktop shell, "DeepSeek Harness", signed under Developer
ID team NAN929V4UM with bundle id com.deepseek.dsh and served from
download.deepseek.com with an electron-updater feed per target. Keep the
Agent id so existing bindings resolve; point everything it names at the
vendor's product.

Resolve the package from the feed (dsh-desk/feeds/mac-arm64/nightly-mac.yml,
dsh-desk/feeds/win-x64/nightly.yml), verify the served bytes against its
sha512, and pin the signature to the vendor: bundle id and Team ID plus
notarization on macOS, publisher on Windows. The previous checks accepted any
valid codesign or Authenticode signature. The host allowlist is reduced to
download.deepseek.com; the GitHub release lookup and dshdesktop.cn mirror go.

Only mac-arm64 and win-x64 are published, so Intel macOS is refused up front
rather than by a 404. Detection matches on the bundle identifier and reports
the version from the bundle on disk, since the app updates itself.

Drop the Unofficial mark and its disclaimer: the row is the vendor's own app.
The disclaimer path is still exercised on a synthetic entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@yujiezhang-ops
yujiezhang-ops requested a review from a team September 26, 2026 08:48
yujiezhang-ops and others added 5 commits September 26, 2026 17:17
…ting

The desktop Agent resolved its dsh document by whether profiles/desktop/
existed. The install flow leaves the app on disk without launching it, so at
first configuration that directory is absent and the write went to the legacy
settings.yaml -- a file the 0.1.7-only desktop app imports only partially on
first start. Resolve the desktop profile to its patch unconditionally and let
the write create the directory: dsh's initProfile fills in package.json and
pnpm-workspace.yaml only where absent, so a patch that arrives first is read
as-is. The web profile keeps its fallback, since npm `latest` is still 0.1.5.

WriteDSHOfficial wrote the credential before validating reasoningEffort, so a
rejected activation had already replaced DEEPSEEK_API_KEY and left a backup.
Validate first, in both layouts.

Smaller: a comment-only scaffold patch keeps its header through the first
write; the patch is encoded with the two-space indent dsh uses, so untouched
rows come back byte-for-byte; the reader takes the first row per id, as the
writer does, instead of merging duplicates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ckage

Supported was decided from the OS alone while the feed lookup also refused by
architecture, so an Intel Mac saw an install entry that failed on every click.
One function now names the vendor's target for a platform and drives both:
mac-arm64 for Apple Silicon, win-x64 for every Windows -- an ARM Windows
machine installs the x64 build, as with the other x64-only desktop apps --
and nothing elsewhere. The macOS inspection and install paths report through
the caller's platform instead of a hardcoded arm64.

The "not offered" test asserted only that Install failed; it now asserts
Supported=false through Inspect too, which is what the UI reads.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The win-x64 installer is signed "Hangzhou DeepSeek Artificial Intelligence
Co., Ltd." (with the period), and .NET quotes that name in the Subject
because it holds a comma. Splitting the Subject on every comma cut the
organization to `"Hangzhou DeepSeek Artificial Intelligence Co.`, so the
genuine installer was refused. Read O= in Go with a quote-aware parser and
pin the publisher observed on 0.1.7-rc.2.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
On first launch the 0.1.7-rc.2 desktop app writes a credential document
with version and records but no refs, and a patch scaffold ending in a
flow-style `[]`. The missing refs made WriteDSH refuse outright, and the
flow style put every appended row on a single line in a file dsh tells
users to edit by hand. Create refs when it is absent and emit an empty
list in block style.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Resolves the conflicts left by #247 landing squashed while this branch still
carried its two commits. dsh.go takes this branch's zip-based install; the
DMG mount path and its darwin test are superseded. writeDSHCredential keeps
both sides: the fresh-install refs creation from this branch and main's
Kind check on an existing refs node.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@yujiezhang-ops
yujiezhang-ops merged commit 8890920 into main Sep 26, 2026
4 checks passed
@yujiezhang-ops
yujiezhang-ops deleted the codex/deepseek-harness-desktop branch September 26, 2026 10:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants