dsh-pet
Desktop pet plugin for DeepSeek Harness (dsh) — grows with your agent token usage; XP economy, custom spritesheets, SSE status push, one-command install
- Stars
- 0
- Language
- TypeScript
- Created
- Aug 24, 2026
- Updated
- Aug 24, 2026
Introduction
dsh-pet — a standalone desktop-pet plugin for DeepSeek Harness
English | 中文
An independent desktop-pet plugin for DeepSeek Harness (dsh, a Cordis-based agent shell). The pet lives in the corner of the Web UI and grows alongside your agent: every assistant/message sums the four token buckets (input/cacheRead/cacheWrite/output) into XP, levels unlock a title ladder, and the agent's live status (thinking / calling-tool / tool-error / answered …) drives the pet's current animation via a millisecond-latency SSE push. Name the pet, swap its spritesheet, scale and drag it anywhere; the hover panel is minimalist monochrome.

The full local dev→deploy loop (two-repo sync, packing, profile install, verification, troubleshooting) lives in DEVELOPMENT.md.
Features
- Grows with token usage — consumption is XP: level, title ladder (a monochrome brightness ramp encodes prestige), and an XP progress bar
- Live status sync — the host projects session events onto coarse statuses (
thinking/calling-tool/tool-ok/tool-error/answered/turn-end) and pushes them over the/pet/eventsSSE stream, so the pet switches animations in step with the page's streaming output (a 3s poll remains as fallback) - Status → action mapping — pick which spritesheet row plays for each status; saved locally
- Custom spritesheets — upload a single image plus a JSON layout config; the host validates cell/frame geometry and rebuilds the manifest
- Rename / resize / drag — pet name, size (50%–200%), and position all persist locally
- Consumption cannot be wiped — tokens/XP/level are recomputed from durable session logs on every boot (ground truth);
/pet/resetaccepts thespritescope only - Minimalist monochrome UI — pure black/white card, serif masthead, mono micro-labels, zero radius, no shadows
Install (one command)
dsh plugin --profile default add \
@dsh-pet/bundle @dsh-pet/host @dsh-pet/client
# then run the shell, e.g.
dsh web --profile default --apiKey <key>
@dsh-pet/bundle is the installer layer: its cordis.patch.yml inserts the pet / ui-pet rows into the composed tree, and installing it activates the layer automatically (appended to dsh.profile.bundles) — no hand-edited yml. After install the host registers the /pet/* routes plus the SSE stream, and the browser half is auto-discovered by dsh-client-modules (via the dsh.client declaration) and served at /plugins/@dsh-pet/client/client.js. Removal is one … remove … command.
All three packages must go in one command: the loader resolves bare package names in patch rows through the profile directory's top-level Node resolution, so both plugin packages must be direct profile dependencies (the bundle's peerDependencies only declare the relationship).
Repository layout
This repo is decoupled from the upstream monorepo: it never depends on the deepseek-harness source tree, only on its published npm packages (pinned exact to the 0.1.0-rc.7 line; framework on @deepseek-ai/cordis@4.0.1). Upstream updates never change this repo's behavior until you re-pin.
| package | path | role |
|---|---|---|
@dsh-pet/host | packages/pet/dsh-pet/ | Host: economy, storage domain, spritesheet validation, /pet/* routes, /pet/events SSE |
@dsh-pet/client | packages/client/ui-pet/ | Browser: the bottom-right desktop-pet overlay (a dsh.client client module) |
@dsh-pet/bundle | packages/bundle/pet/ | Installer: the one-command composition patch |
dsh-pet/
├─ package.json / pnpm-workspace.yaml / vitest.config.ts / tsconfig.base*.json
├─ config/tsdown-client.ts # standalone client-bundle renderer (module-loader closure, CSS inline)
└─ packages/
├─ pet/dsh-pet/ # host: service, domain, economy, spritesheet, /pet/* routes
│ ├─ src/{index,spec,types,economy,catalog,usage,default-sprite,spritesheet,invariant}.ts
│ └─ tests/{catalog,economy,usage,default-sprite,spritesheet}.spec.ts
├─ client/ui-pet/ # browser: the pet overlay; consumes @dsh-pet/host types
│ ├─ src/client/{PetCompanion,PetCompanion.module.css,agent-status,titles,locales,pet-api,slots,spritesheet,index}
│ └─ tests/spritesheet.client.spec.ts
└─ bundle/pet/ # installer: package.json + cordis.patch.yml (no code)
Commands
pnpm install # resolves all @deepseek-ai/* deps from npm (pinned)
pnpm build # tsc host → lib/types · tsc client · tsdown client bundle → lib/client.js
pnpm typecheck # tsc --noEmit, both packages
pnpm test # vitest: host economy/usage/spritesheet/catalog + jsdom pet spec (85 tests)
How the client half is served
The browser half is a standalone client module (dsh.client declaration + lib/client.js). At runtime dsh-client-modules serves it on demand at /plugins/<package-name>/client.js; the SPA lazy-fetches it, so installing a new client package does not require recompiling the web bundle. Published client packages ship only lib/; ./src/* exports are source-launch placeholders and are not loadable from a published tarball — deep …/src/… imports are removed here.
Publishing (maintainer-run — needs npm credentials)
All three packages publish to the @dsh-pet scope. Use pnpm publish (not npm publish): pnpm normalizes the intra-workspace workspace: specs (client → host, and the bundle's peers) to real versions.
# host first, then the browser half, then the bundle (its peers point at the first two)
pnpm -C packages/pet/dsh-pet publish
pnpm -C packages/client/ui-pet publish
pnpm -C packages/bundle/pet publish
Once on the registry, the local install command switches from file: tarballs to bare package names (no pnpm file:-cache quirks). Keep the three packages' versions in lockstep.
Live boot
The end-to-end run (dsh web → the floating pet appears bottom-right → send a message → XP grows and titles change → the pet switches animations in step with the streaming output → upload a spritesheet / rename / resize) requires a running harness and an API key. Everything short of the live provider call is exercised by the test suite; the full local-tarball→profile install chain has been verified live (see the curl checklist in DEVELOPMENT.md §3.7).
Version policy
Dependencies are pinned in each package's package.json. To absorb a harness release: bump the @deepseek-ai/dsh-* pins and the framework lines together, re-run pnpm install && pnpm build && pnpm test, and re-verify that the client bundle's external platform-module set (in config/tsdown-client.ts) still matches what the new harness serves.