Skip to content
dsh.fish
Bundle

dsh-sound

DeepSeek Harness plugin: per-event task-completion and attention sounds for turn-end / approval / question / plan-review / goal-blocked / task-failure, each with its own sound (built-in synth, local audio file) and volume (Web UI)

Source
AI-Galaxy-GPU
stars
8 stars
License
MIT
Updated
Updated 2 days ago

Readme

# dsh-sound

English | [δΈ­ζ–‡](README.zh.md)

A DeepSeek Harness (DSH) plugin for the **Web UI**: play a customizable sound when a task
finishes, and an attention sound whenever something needs a human.

> πŸ“¬ **Submitted to [Awesome DSH Plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)** β€” [PR #752](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/752) (pending maintainer approval)

## Demo

<video src="https://github.com/user-attachments/assets/4441d222-5b74-4598-895e-fd5678641ee7" controls></video>

Source file: [`docs/demo.mp4`](docs/demo.mp4)

## Screenshots

![dsh-sound settings](assets/screenshots/sound-image.png)

## Features

### Six independent events

Each event type has its **own sound and its own volume** (0–100%):

| Event | Detection | Default sound |
|---|---|---|
| Turn end / background task completed | `turn/end` with `reason.kind === 'completed'`; job β†’ `completed` | Chime |
| Approval request | `approval/requested` frame | Ding |
| User question | `question/requested` frame (not plan-review shaped) | Ding |
| Plan review | `question/requested` frame classified as plan-review | Ding |
| Goal blocked | goal projection enters `blocked` | Ding |
| Background / loop failure | job β†’ `failed`; `turn/end` `error`; `host/agent-error` | Bell |

- **Completion** respects the β€œquiet current session” option; **attention** events (approval /
  question / plan-review / goal-blocked / failure) always ring.
- User abort, killed jobs, and `max-tokens` / `blocked` / `interrupted` turns stay silent.
- Events come from `events.mux` plus `events.host`. Mux-open replay of still-pending
  approval/question frames (refresh recovery) does not ring; each `rpcId` rings at most once.
- When the event stream is unavailable or frames fail to unwrap, the plugin falls back to
  snapshot diffing.
- **Multiple tabs**: the same event rings only once (BroadcastChannel tie-break). A single
  tab plays immediately, with no 40 ms handshake.

### Sound sources

Each event picks one sound from a **Radio.Button group** in the settings panel:

- **Built-in synthesized sounds** β€” Ding / Chime / Bell / Complete / Success, generated live
  with Web Audio, no audio files required.
- **Mute** β€” silence that event.
- **Local file** β€” choose any MP3/WAV/etc. file; a **file picker appears below the event row**
  once selected. Uploaded files are stored in IndexedDB (`dsh-sound-audio`), immune to the
  localStorage quota.

### Settings panel (Settings β†’ ε£°ιŸ³ι€šηŸ₯ / Sound Notification)

Master switch, six event rows (volume slider on the title row; Radio.Group of Radio.Button
options below β€” click to select and play; a local-file row with a choose/replace
button appears when ζœ¬εœ°ζ–‡δ»Ά is selected), and config export/import.

## Install

Requires a DSH version with bundle-plugin support (`dsh.profile.bundles` + `dsh.bundle.patch`)
and `pnpm` on PATH (`corepack enable` or `npm i -g pnpm`).

```sh
# One command: pnpm installs the package and adds it to the profile's bundle layer
dsh plugin --profile web add dsh-sound

# Restart the server (or refresh the page), then open Settings β†’ ε£°ιŸ³ι€šηŸ₯
```

Other profiles work the same way: `dsh plugin --profile <name> add dsh-sound`.

### Manual install (without pnpm)

1. Copy this package and its runtime deps (`@deepseek-ai/schemastery`,
   `@deepseek-ai/cosmokit`, `@standard-schema/spec`) into
   `$DSH_HOME/profiles/web/node_modules/`.
2. Append `"dsh-sound"` to `dsh.profile.bundles` in
   `$DSH_HOME/profiles/web/package.json`.
3. Restart `dsh web`.

## Configuration storage

- The browser half persists its configuration in **localStorage** (key
  `dsh-sound:config`), sanitizing every read and filling defaults. Uploaded
  local music files are stored in **IndexedDB** (`dsh-sound-audio`) instead β€”
  no localStorage quota limits.
- Config keys: `enabled`, `quietCurrent`, and six sound + six volume fields β€”
  `completionSound` / `approvalSound` / `questionSound` / `planReviewSound` /
  `goalBlockedSound` / `failureSound` (builtin key, `none`, `local`, `data:` URL,
  or `audio:<id>`) and `completionVolume` … `failureVolume` (0–1).
  `localFiles` keeps the last chosen local file per event so switching to a
  built-in sound and back does not drop it.
- **0.2.0 migration**: older configs are upgraded automatically β€” `defaultSound`
  becomes `completionSound`, voice/TTS values degrade to the per-event default,
  and `workspaces` / `debounceMs` / global `volume` / voice settings are dropped.
- **Export / import**: the settings panel downloads the full config as JSON
  (IndexedDB audio references are inlined as data URLs) and restores it from a
  JSON file β€” moving browsers or machines does not require reconfiguration.
- The host half also registers the `dsh-sound` settings namespace: on rc.6 the
  settings API allowlist (`WEB_SETTINGS_NAMESPACES` in `dsh-host-apiproxy`) does not expose
  third-party namespaces to browsers, so the client does not depend on `settingsScope` today;
  the registration keeps the migration path open for future releases.

## Development

```sh
npm test      # 100+ assertions across host and client halves (Node only, no browser needed)
npm run check # syntax check
```

Layout:

> **Profile development note**: the profile's pnpm uses `nodeLinker: hoisted`,
> which COPIES `file:` dependencies into `node_modules` at install time β€”
> edits to this checkout do not reach the running app until you either
> re-run `pnpm --dir ~/.dsh/profiles/web update dsh-sound` or replace the
> copied directory with a symlink to this checkout. The web server reads
> bundle content per request (only the boot-page rev hash is cached at
> startup), so after refreshing the copy a browser hard-refresh (Cmd+Shift+R)
> is enough β€” no server restart needed.

- `lib/index.js` β€” host half: registers the settings namespace (schema + defaults)
- `lib/client.js` β€” browser bundle: event detection, sound engine
  (Web Audio / IndexedDB audio), settings panel
- `lib/types/index.d.ts` β€” host-side type declarations
- `cordis.patch.yml` β€” bundle patch layer (inserts the `dsh-sound` row)
- `tools/` β€” tests and verification scripts (not shipped in the npm package)

## Publish

```sh
npm login                       # npm account (2FA recommended)
npm publish --access public
```

## License

[MIT](LICENSE)

Install

dsh plugin --profile web add github:AI-Galaxy-GPU/dsh-sound#92deeda22be4a7d7f3f2d75be0922e18d26e86ca

Profile: web

Source