Preface¶
When running an Agent in DSH, most of the time you stare at the Web GUI: you have to switch back to the page to see where the task is in the step, what tools were called, and how many Tokens were burned today. The web window cannot serve as a character that always floats on the desktop and allows interactive clicks. dsh-desktop-pet-naiwa is a Windows client plugin maintained by Night-maze that projects this information onto a transparent, borderless, always-on-top native window. At the same time, this window supports single-click interaction, long-press dragging, edge-snapping semi-hiding, and dragging files into the Recycle Bin.
Below is an introduction to this plugin: first, why it is split into two layers, followed by its features, installation commands, and available configurations.
What is this¶
One-sentence positioning: A Windows desktop pet (desktop pet) based on the DeepSeek Harness plugin mechanism, maintained by Night-maze. Category: Client. License: MIT. GitHub repository created on 2026-11-05. Current stars: 0. NPM package name: dsh-desktop-pet-naiwa. Version: 1.0.0.
It solves the problem: Agent runtime state is absent from the native desktop. Existing DSH client plugins can only render the React interface inside the Web GUI and cannot create arbitrary OS windows.
Why Host Plugin + WPF Subprocess¶
DSH client plugins run in a browser sandbox and do not have permission to create native windows. To create a transparent, borderless, always-on-top, draggable window, an independent native process is required. Therefore, the plugin is split into two layers:
- Host Plugin
lib/index.js(Cordis bundle): Subscribes tosession/event, maintains a daily backfilled token ledger, reads the real balance, and is responsible for launching and supervising the subprocess. - WPF Subprocess
pet/DesktopPet.ps1+DesktopPet.cs: Draws the window itself and handles gestures and animations. - Communication: Between the two, they communicate via newline-delimited JSON on the 127.0.0.1 loopback with random token authentication.
The responsibilities have clear boundaries: The Host does not hardcode Token, balance, or progress; the WPF subprocess does not access the network or read DSH internal state, only consuming the JSON sent by the host.
Core Features¶
Agent Progress Projection¶
Projection based on events: turn/start indicates the task has started, step/start and tool/call provide progress, assistant/message and assistant/attempt provide Token usage, turn/end gives completion, failure, or cancellation, and agent/error serves as a fallback for failure.
By default, it shows an indeterminate progress bar plus “Step N · Tool Name” text. The plugin explicitly does not use fabricated 0-100% numbers to avoid presenting a percentage that looks like progress but has no basis.
Token Cost Estimation & Real Balance¶
“Today’s Cost” is Real Token Usage × Public List Unit Price. This is marked with ≈ in the UI, as it is an estimated value. The usage ledger deduplicates by sessionId:seq; successful messages for the same turn/step replace failed attempts; attempts after llm/retry-started are allowed to be counted again. Data is backfilled from ctx.sessionPersistence on startup.
“Balance” comes from ctx.deepseekAccount.getBalance. This is the real value from the platform wallet, not estimated or fabricated. If reading fails, the previous value is retained and marked as stale. Refreshing uses exponential backoff, never using 0 as a substitute.
Prices default to built-in CNY/USD public list prices for deepseek-flash and deepseek-v4-pro, distinguishing peak hours (Beijing Time weekdays 9–12, 14–18) and off-peak hours. Models without a configured unit price only display Tokens without an amount.
Interaction Behaviors¶
- Click: Randomly selects one
interactionsconfiguration item based on weight. There are 6 built-in types (blink, jump, shake, heart, random line, dance), with configurable weights, animations, dialogue, and corresponding sprites. - Long Press Lift: Pressing for more than
longPressMs(default 450ms) counts as lifting, after which it acts like a pendulum/spring drag and naturally drops back when released. - Edge Semi-Hide: After dragging to the screen edge and releasing, it determines the direction based on the current monitor’s
WorkingAreaandedgeThreshold, sliding to reveal onlyedgeVisibleRatio; the mouse exits when hovering over it, and it slides back after a delay when the mouse leaves. - Click-Through:
WM_NCHITTESTdetermines this based on the current sprite’s alpha. Transparent pixels returnHTTRANSPARENT, preventing it from blocking other desktop operations.
Drag Files to Recycle Bin¶
Implemented via IFileOperation with FOF_ALLOWUNDO | FOFX_RECYCLEONDELETE. It forces “Recycle or Fail” and has no File.Delete / Directory.Delete fallback. Paths are restricted: drive root, system directories, symbolic links/junctions, no permissions, file in use, cross-drive or network recycle failures will preserve the original file and pop up a clear error. AppContainer or low-integrity processes will be rejected.
Client Side¶
The client side registers a sidebar 🐾 Desktop Pet entry and a settings page. You can adjust scale, volume, stats display, long-press/drag thresholds, and support refreshing stats, restarting, and showing/hiding. Settings are written to the data directory ui-config.json, overriding the same-named field in the profile; deleting this file and restarting the plugin restores the profile configuration.
Installation & Enablement¶
Prerequisites: Windows 10/11, system-installed Windows PowerShell 5.1 and .NET Framework 4.x (WPF components). No extra npm dependencies are required; the Host only uses Node built-in modules. The plugin declares compatibility with the DSH 0.2.1-rc.5 Cordis plugin/service API.
The recommended way is to install via bundle. First, open Plugins in the DSH Web GUI and execute:
dsh plugin --profile desktop add dsh-desktop-pet-naiwa
Or point to a local source code directory:
dsh plugin --profile desktop add link:F:\path\to\dsh-desktop-pet-naiwa
package.json already declares the bundle patch and client injection:
"dsh": {
"bundle": { "patch": "./cordis.patch.yml" },
"client": { "platform": "web", "inject": ["..."] }
}
The next steps:
- Open the “Desktop Pet - Baby Naiwa” bundle on the Plugins page, or add it to the
dsh.profile.bundlesin your profile. - Remove any lines pointing to absolute paths to avoid loading the same plugin twice.
- Refresh the DSH page; the sidebar should appear with 🐾 Desktop Pet. If not, restart DSH once to let the client graph rescan
dsh.client.
There is another dev mode approach, appending an absolute path entry to %USERPROFILE%\.dsh\profiles\<profile>\cordis.patch.yml. It has no bundle identity, no toggle on the Plugins page, and no client settings page; it is only suitable for temporary debugging, and should be deleted for daily use.
Configuration¶
See config.example.yml in the repository for full fields and comments. Common items:
| Field | Default | Description |
|---|---|---|
enabled |
true |
Plugin master switch |
imagePath |
'' |
Single custom avatar; leave empty to use the built-in 12 sprites |
spriteDir / soundDir |
Built-in assets | Relative to plugin directory or absolute path |
scale |
1.0 |
Overall scale 0.5–2, scales window, UI, and avatar together |
volume |
0.7 |
SFX volume 0–1 |
topmost |
true |
Always on top |
avoidFullscreen |
true |
Hide when full-screen apps are active |
statsDisplay |
always |
always / hover / off |
longPressMs |
450 |
Long press lift threshold |
dragStartPx |
10 |
Drag start threshold |
edgeThreshold |
24 |
Edge triggering distance (physical pixels) |
edgeVisibleRatio |
0.38 |
Ratio visible after semi-hiding |
currency |
auto |
CNY / USD / auto (follows wallet) |
costPrecision |
4 |
Cost decimal places |
balanceRefreshMs |
60000 |
Balance refresh, minimum 30s, fails over to 5min with exponential backoff |
statsRefreshMs |
30000 |
Stats push interval |
pricing |
Public price list | Peak/off-peak/holiday/unit price per million Tokens |
interactions |
6 types | Weights/animations/dialogue/sprites for random click interactions |
protectPaths |
System directories | Path prefixes that refuse recycling |
backfill |
Enabled | Count/bytes/timeout limit for startup backfill |
pricing.holidays defaults to an empty array: Chinese statutory holidays change every year, and the plugin does not embed an easily expiring calendar; if precise peak/valley billing is needed, manually fill in YYYY-MM-DD.
Arrays in the configuration are full replacements, not merged item by item. Clicking draws one item from interactions and uses that item’s sprite to specify which image to display (filename without .png). To keep a certain number of items, list them all; unlisted items will not appear.
Data & Logs¶
Default directory: $DSH_HOME/storages/desktop-pet/:
| File | Description |
|---|---|
pet-config.json |
Merged configuration written by Host to WPF |
usage.json |
Today + last 14 days token ledger, including model, time, and four buckets |
host.log |
Host plugin diagnostics |
pet.log |
WPF / Launcher diagnostics |
launcher.log |
Launcher compilation and startup diagnostics |
window.json |
Last window position, used for multi-monitor recovery |
Logs may be rotated to pet.log.old.<timestamp>; the plugin will not delete user files.
Testing & Verification¶
node test/run-tests.mjs # host unit tests
node test/mock-host.mjs # real WPF + mock Cordis integration
powershell -STA -File scripts/demo.ps1
The Recycle Bin test must run in a normal user process, not inside the DSH workspace sandbox:
powershell -STA -File scripts/test-recycle.ps1
Use Cases & Notes¶
Suitable for developers running DSH Agents on Windows: those who want progress and cost to stay visible on the desktop, while accepting a persistent subprocess. In fullscreen demo scenarios, avoidFullscreen will hide it and not interrupt the presentation.
Points to note:
- The plugin runs with the permissions of the current dsh process. It does call the platform balance interface and operates the Recycle Bin when files are dragged in, so you should review the source code before installing to ensure the
ctx.deepseekAccount.getBalancecall andIFileOperationcall meet your expectations. The license is MIT, source is from the npm package and GitHub repository. - The only paths that will delete files are the Shell Recycle API. If the Shell cannot recycle, the operation fails directly, and the original file is preserved; there is no fallback for permanent deletion.
- “Today’s Cost” is just an estimate; the amount is calculated based on the public list price, which does not equal the settled amount.
- The progress bar deliberately does not show a percentage. If you need exact numbers, you should still check the Web GUI or logs.
Conclusion¶
This plugin moves the Agent runtime state from the web page to a native window. The key trade-offs are: using estimated values for cost and real values for balance, and using an indeterminate progress bar instead of fabricated percentages. The two-layer structure (Host Plugin + WPF Subprocess) is the way it bypasses the client plugin sandbox restrictions, and is also what needs to be understood during installation and troubleshooting.
Directory Page: https://www.skillhub.cn/plugins/Night-maze/dsh-desktop-pet-naiwa
Repository: https://github.com/Night-maze/dsh-desktop-pet-naiwa
NPM: https://www.npmjs.com/package/dsh-desktop-pet-naiwa