Preface¶
DeepSeek Harness (dsh for short) is designed with the principle of “everything is a plugin”: models, tools, sessions, and UIs can all be attached to its runtime. The default smart agent in the terminal still relies on typing for input and output—you can’t dictate a command when away from the keyboard, and you have to come back to stare at the logs after a long task finishes. While the community has developed many visual and browser plugins, voice support still requires separate installation.
dsh-voice addresses these two daily pain points: it converts dictated audio into user messages and has the smart agent read its replies aloud. It does not build a complete audio pipeline from scratch, but instead builds on top of dsh’s existing ctx.shell, ctx.jobs, ctx.settings, ctx.attachments, and ctx.conversationEvents. This article is collated after cross-checking with the community plugin directory page, GitHub repository README/README.zh.md, package.json, and cordis.patch.yml. Two points need clarification first: the community plugin directory is an independent site and has no official affiliation with DeepSeek/HyperFount; Harness itself is still in developer preview, and incompatible changes may occur in the future. There are other dsh-voice repositories on GitHub with different feature scopes; this article only covers the one maintained by Jesse-njx.
What is it¶
dsh-voice is a DSH plugin categorized as “Tools & Capabilities”, maintained by Jesse-njx, licensed under MIT, and primarily written in TypeScript. The package name in the repository’s package.json is @dsh-voice/bundle, version 0.1.0, requiring Node.js >=20. As verified on 2026-08-18, both the directory page and GitHub API show 1 star; the repository was created on 2026-08-13, and it was added to the community directory on 2026-08-14; topics include deepseek-harness, dsh-plugin, stt, tts, and voice.
One-sentence positioning: Take voice notes as input, read out answers as output. Dictated audio will be converted into user messages (transcription), and the smart agent can also read its replies aloud (synthesis); for long-running tasks or headless runs, it can also leave a voice narration. Audio files are stored as regular files under ~/.dsh/voice/ by default, and session logs only save references and transcribed text.
It does not solve the problem of “real-time voice chat with a microphone”. The README clearly lists the non-goals for v0.1: it will not support real-time streaming conversations, outgoing voice calls, wake words or persistent listening, nor will it write raw audio into session logs. Recording and playback must wait for the model to explicitly call the tools, and it will not speak on its own by default.
Core Features¶
The plugin package registers two tools, one persistent event, and one session toggle. The web client supplements audio cards. The plugin ID in cordis.patch.yml is dsh-voice, and the name is @dsh-voice/bundle.
transcribe: Turn dictation into user messages¶
transcribe({ source, to? }) performs speech-to-text transcription. source must select one of the two options, and both cannot be passed at the same time:
- { file: }: Transcribe an existing audio file.
- { record: { seconds? } }: Record a segment from the microphone, defaulting to 5 seconds. The recording path requires ffmpeg, or a bundled swift shim on macOS.
The transcription result is inserted as a user message, not tool output. A voice/note event will be recorded in the session, and the web side will render it as an audio card with play/pause controls, duration, backend logo, and transcribed subtitles. The tool itself returns a compact handle { transcript, audioRef, backend, durationMs } for easy access to structured data in Code Mode.
If dsh-crosstalk is also installed, you can add the to: parameter to send this voice note as a tagged peer message to another local session, along with the audio path. This parameter will not appear when crosstalk is not installed.
speak: Background reading without blocking the current turn¶
speak({ text, voice?, rate? }) performs text-to-speech synthesis. Synthesis and playback run as background tasks via ctx.jobs, with kind voice-speak. The tool immediately returns { jobId, audioRef } without blocking the current turn; playback is asynchronous, and failures will trigger a notification instead of throwing an error into the current conversation.
Each backend first writes a persistent file to audioDir, then attempts playback. Since it is just a regular tool on ctx.jobs, it can also be called by routines and headless runs. The README also uses it as a long-task narration: for example, saying “Build completed, 0 failures” when a build finishes. Narration is not a separate API, just a call to speak within the job context.
/voice and readReplies¶
A session-level toggle to automatically read the smart agent’s replies. The configuration item readReplies defaults to false; you can change it in-session using commands:
/voice on
/voice off
/voice status
/voice speak <text>
The meanings of each command are as follows:
- on: Start reading assistant replies.
- off: Stop reading.
- status: View the current toggle status, backend, and audioDir.
- speak <text>: Read a line of text directly from the input box without waiting for the model to call the tool.
The toggle takes effect for the current session, and you do not need to reinstall the plugin after changing it. readReplies only reads existing replies and does not record audio.
How to select a backend¶
Speech-to-text transcription is handled by the dsh-voice-backends module:
- whisper-local: Uses the whisper.cpp binary on PATH (can also be specified in configuration), called via ctx.shell, fully offline.
- openai: Accesses an OpenAI-compatible whisper-1 endpoint via standard credential channels, with the key environment variable OPENAI_API_KEY. This is the only STT path that will send audio outside the local machine, and it will only be enabled with explicit configuration.
- macos: Calls the system SFSpeechRecognizer via a bundled swift shim, also via ctx.shell. No additional software installation is required, and no network configuration is needed.
- **fake``: A text-to-text test fixture. If the file content isor the file name is in the formfixture-.m4a`, the transcription result will be that text. Used for CI to validate the full tool chain without touching the microphone or network.
Text-to-speech:
- say (default): Uses macOS’s say -o --file-format=m4af --data-format=aac, then plays the result with afplay. Zero installation required, outputs m4a files playable in Chrome/Safari.
- piper``: Local Piper binary, offline neural TTS.
- **edge-tts**: Cloud-based; only used when explicitly configured.
- **fake`: Writes fixed JSON to allow the speak output to go through fake STT again.
The selection rule is a pure function, with unit tests in the repository: use whichever backend is configured; if none are configured, fall back to offline options first, with STT falling back to whisper-local → macos and TTS falling back to say → piper. Cloud backends will never be automatically selected. If the local machine has neither whisper.cpp/macOS speech recognition nor say/Piper, a clear error message will be given telling you what to configure, instead of silently switching to OpenAI or edge-tts.
Local-first: Files on disk, logs only store references¶
The design centers on local-first principles:
- Unless explicitly setting stt.backend: openai or tts.backend: edge-tts, audio will not leave the local machine.
- Every output is a file directly viewable and deletable via rm under audioDir (default ~/.dsh/voice/).
- Session logs only save one voice/note entry: noteId, turn coordinates, audioRef (path + mime + durationMs), transcript, direction: 'in' | 'out', and backend. There are no update events in v0.1.
- If the audio file is missing or deleted, the web card will downgrade to only displaying the transcribed text. Playback does not require reloading the audio file.
The web client renders inbound notes (STT) as user turns, and outbound speak calls as agent-side cards. The client bundle is built as lazy-CJS via scripts/build-client.mjs, and after installation to the web profile, it is served via /plugins/@dsh-voice/bundle/client.js.
Installation and Activation¶
The installation command provided on the community directory page can be run in the DeepSeek Harness terminal:
dsh plugin add github:Jesse-njx/dsh-voice
For reproducible installations, pin the commit hash as specified on the directory page:
dsh plugin add github:Jesse-njx/dsh-voice#<commit>
Replace <commit> with the actual commit SHA from the repository.
The repository README also provides an installation method using the npm package name and specifying the web profile:
dsh plugin --profile web add @dsh-voice/bundle
As of 2026-08-18, @dsh-voice/bundle cannot be found on the npm registry (returns 404), do not use this as the current valid installation method. Installation should still follow the GitHub command from the directory page. The README includes --profile web because the web audio cards need to be loaded into the web client; the directory page command itself does not include a profile. If you cannot see the audio cards in the conversation after installation, confirm that you are using a profile with a Web UI, not just the host tooling.
The security disclaimer from the directory page still applies: the plugin runs with the permissions of the current dsh process, and may execute code during installation. Please review the source code and license before installing. The prepare/prepublishOnly scripts in package.json will run pnpm build. You can confirm if dsh-voice appears in the list of installed plugins; some environments require restarting Harness for changes to take effect. Recording and playback will not start automatically until the model calls the tools.
Configuration¶
All fields are optional, and can be written in the profile patch or cordis.patch.yml. The complete configuration shape provided in the README is as follows:
plugins:
dsh-voice:
stt:
backend: whisper-local | openai | macos | fake
model: whisper-1
whisperLocal: { bin: whisper-cli, model: tiny }
openai: { baseUrl: https://api.openai.com/v1, apiKeyEnv: OPENAI_API_KEY }
tts:
backend: say | piper | edge-tts | fake
voice: Samantha
rate: 180
piper: { bin: piper, model: /path/to/model.onnx }
edgeTts: { voice: en-US-GuyNeural }
readReplies: false
audioDir: ~/.dsh/voice
Default behaviors: STT automatically selects offline backends (whisper-local → macos), TTS defaults to say (falling back to say → piper), readReplies is false, and the audio directory is ~/.dsh/voice. The OpenAI key uses the standard credential channel OPENAI_API_KEY, falling back to the startup environment variable. The repository’s built-in cordis.patch.yml only inserts empty stt/tts sections, readReplies: false, and the default audioDir, leaving specific backend configurations for users to override themselves.
Typical Usage¶
The repository does not include fabricated conversation scripts; reproducible entry points are the tool parameters and /voice commands.
When transcribing an existing file, only include file in source; when recording a few seconds from the microphone, only include record:
{ "source": { "file": "/path/to/note.m4a" } }
{ "source": { "record": { "seconds": 5 } } }
To have the smart agent read a piece of text:
{ "text": "build finished, 0 failures" }
voice and rate are optional, and will use the default voice and speed from the configuration if not provided.
To enable automatic reading of replies in a session, enter directly:
/voice on
To view the current backend and directory:
/voice status
The recommended workflow is: first confirm that you have available offline backends on the local machine (whisper.cpp or macOS speech recognition, and say or Piper), then use /voice status to verify, and finally have the model call transcribe/speak. Do not set stt.backend to openai immediately unless you explicitly accept that audio will leave the local machine.
Applicable Scenarios and Notes¶
It is suitable for the following situations:
- You are away from the keyboard and want to dictate a command, with the transcription result entering the conversation as a regular user message.
- After a long build or headless task finishes, use speak to leave a narration without staring at the terminal.
- You want replies to be read aloud, but keep them quiet by default: use /voice on to enable it per session.
- You care where audio is stored: all files are under ~/.dsh/voice/, and logs only contain references and transcriptions.
Before using it, treat the following as hard limitations rather than “may be fixed later”:
1. Permissions and supply chain. The plugin runs with the permissions of the current dsh process, can call shell commands, read and write audioDir, and send audio outside the local machine if cloud backends are configured. Check the source code at https://github.com/Jesse-njx/dsh-voice and the MIT license before installing; pin the commit hash for reproducible environments.
2. Not real-time voice conversation. v0.1 explicitly does not support streaming two-way chat, wake words, persistent listening, speaker separation, outgoing calls, or writing raw audio to session logs.
3. Default backend favors macOS. TTS defaults to the system say; the STT fallback chain includes macos. On Linux, you usually need to set up whisper.cpp and Piper yourself, otherwise you will get a clear error message telling you what to configure, instead of automatically switching to cloud backends.
4. Recording has prerequisites. { record } depends on ffmpeg, or a bundled swift shim on macOS. Without a recording path, this branch will not be available, but you can still use { file } to transcribe existing audio.
5. Cloud backends require explicit selection. openai and edge-tts will only be enabled if explicitly added to the configuration. Installing the plugin does not mean that voice data has been sent to third parties.
6. Web cards depend on the web profile. The host-side tools and /voice commands are not part of the web audio card half of the plugin. Installing it only in a headless profile will result in no cards being displayed, which aligns with the README description.
7. The project is very new. The repository was created on 2026-08-13, version 0.1.0, with very few stars. The README states that the test suite has 46 cases covering parameter schemas, backend selection, fake end-to-end testing, and card downgrading, but do not treat it as a production-grade voice suite.
Summary¶
dsh-voice adds a thin layer of voice input and output to DeepSeek Harness: transcribe turns dictation into user messages, speak reads aloud in the background without blocking turns, and /voice controls whether to automatically read replies per session. Audio is stored as regular files on disk, and logs only save references; cloud backends will never be automatically selected. It is not a real-time voice chatbot or a WeChat voice solution; its boundaries are listed in the README’s non-goals list. Using it with the expectation that “tools are called to produce sound, and local files can be deleted” will lead to a more stable experience.
- Directory page: https://deepseek-harness-plugin.com/zh-CN/plugins/dsh-voice/
- GitHub: https://github.com/Jesse-njx/dsh-voice
- DeepSeek Harness: https://github.com/deepseek-ai/deepseek-harness