dsh-escutar
Manifest validDeepSeek Harness plugin that checks whether a voice note's transcript matches its audio (EmbeddingGemma cosine in a shared 768-d space) and searches the audio by meaning.
dsh-escutar
A voice note has two records: the audio and the transcript. When the transcript is wrong (wrong language, noise, text from another note), the mistake enters the conversation silently. This plugin measures how far the transcript is from the audio in the shared 768-dimensional space of EmbeddingGemma, and lets you search the audio by meaning even when the transcript cannot be trusted.
It never transcribes again, never deletes and never rewrites a transcript: it reads, measures and warns. Audio never leaves your machine.
Install
Works on Linux, macOS and Windows. Open a terminal (PowerShell is fine on Windows) and run:
# 1. pnpm — the harness manages profile plugins with it
npm install -g pnpm
# 2. add the plugin to your `web` profile
npx @deepseek-ai/dsh plugin --profile web add github:songjongdevsecops/dsh-escutar
# 3. restart the harness: the bundle layer is read at boot
# stop `dsh web` (Ctrl+C) and start it again
npx @deepseek-ai/dsh web
If dsh is installed globally, step 2 is
dsh plugin --profile web add github:songjongdevsecops/dsh-escutar.
The package declares dsh.bundle.patch, so dsh plugin add also appends the
plugin row to the profile bundle list. That row is the only activation, and it
is applied at every boot. Nothing else to configure to install.
Update and uninstall
# update: re-run the add command above (it resolves the repository again), then restart
npx @deepseek-ai/dsh plugin --profile web remove dsh-escutar # then restart
Requirements
- Node.js
^22.19.0 || >=24.0.0(the same range DeepSeek Harness supports). - pnpm on
PATH— the harness uses it to manage profile plugins. - DeepSeek Harness itself.
- EmbeddingGemma CLI reachable as an executable. The plugin calls
parecido <audio> <text> --simetricoandbuscar <query> --indice <collection> --n <n>. Point it with theDSH_ESCUTAR_GEMMA_BINenvironment variable or thegemmaconfig key. If the binary is missing, the first measurement answers with an error naming that variable; the rest of the harness is unaffected. - A voice corpus: one audio file and one transcript per note, sharing the
id (
001.ogg+001.md), either side by side or split intoaudio/andtexto/.
The plugin itself has no native dependencies and no build step: plain ESM using only Node built-ins.
Usage
| Who | How | What you get |
|---|---|---|
| the agent | conferir_transcricao tool | check one note (id) or search the audio by meaning (consulta) |
| you | /escutar command | recent notes with a suspicious transcript |
| the agent | agent/pre-step hook | a short warning at the end of the conversation when a recent note is suspicious; nothing is appended when there is no suspicion, so it costs zero tokens |
conferir_transcricao { "id": "001" } # is this note trustworthy?
conferir_transcricao { "consulta": "what did I say about backup" } # search the audio
/escutar # recent suspicious notes
Verdicts
The verdict is the cosine between the audio vector and the transcript vector:
| Verdict | When |
|---|---|
suspeita | cosine < 0.78 − 0.03 |
incerta | inside the 0.03 band around the threshold |
confiavel | cosine > 0.78 + 0.03 |
sem-medida | no number (CLI failure, corrupt cache) |
A note on the edge of the threshold is reported as incerta on purpose: it is
neither an alarm nor a pass.
Configuration
Every key falls back to a default, so a wrong config never breaks the plugin.
Override the row by id in your profile patch
(~/.dsh/profiles/web/cordis.patch.yml; on Windows,
%USERPROFILE%\.dsh\profiles\web\cordis.patch.yml). A patch replaces the
whole config of the row, so restate every key you want to keep:
- id: escutar
config:
corpus: '/path/to/voice-corpus'
cache: '/path/to/discordancia.json'
gemma: '/path/to/embeddinggemma'
limiar: 0.78
banda: 0.03
maxAvisos: 2
colecaoAudio: 'escutar-audio-gemma-v1'
tempoLimiteMs: 120000
| Key | Default | Meaning |
|---|---|---|
workspace | package directory | base for the corpus and cache defaults |
corpus | <workspace>/corpus | folder with the audio/transcript pairs |
cache | <workspace>/estado/discordancia.json | cache read by /escutar and the hook |
gemma | DSH_ESCUTAR_GEMMA_BIN, then ~/.local/bin/embeddinggemma (embeddinggemma.exe on Windows) | EmbeddingGemma CLI |
limiar | 0.78 | cosine below which the transcript is suspicious |
banda | 0.03 | uncertainty margin around the threshold |
maxAvisos | 2 | maximum lines the hook appends; 0 disables it |
colecaoAudio | escutar-audio-gemma-v1 | index name used by consulta |
tempoLimiteMs | 120000 | timeout for one CLI call |
The DSH_ESCUTAR_GEMMA_BIN variable is used when gemma is not set in the
config. The package carries no machine-specific path.
Index and cache
The plugin reads two artifacts; it does not build them:
-
Audio index (
colecaoAudio, defaultescutar-audio-gemma-v1) — needed only byconferir_transcricaowithconsulta. Build it with the EmbeddingGemma CLI:embeddinggemma indexar /path/to/corpus/audio \ --nome escutar-audio-gemma-v1 --ext ogg,opus,mp3,wav,m4aThe audio encoder reads roughly the first 30 seconds of each file. If you need long notes sliced into windows, slice them before indexing.
-
Discordance cache (
cache) — needed by/escutarand the hook. It is a JSON file written by your indexing pipeline:{ "gerado_em": "2026-10-13T12:00:00+00:00", "notas": [ { "id": "001", "cosseno": 0.84, "transcricao": "first 200 characters of the transcript", "mtime": "2026-10-13T11:59:00+00:00", "mtime_texto": "2026-10-13T11:59:00+00:00", "janelas": [0.84], "segundos": 12 } ] }mtimeandmtime_textoare the modification times of the audio and the transcript when the cosine was measured. The tool checks them on every read: a cache without them, or older than the files on disk, is not served as current — the note is measured again on the spot. The hook only warns about notes whosemtimeis within the last 48 hours.
Without an index or a cache, conferir_transcricao with id still works: it
measures the pair live (about 11 s to load the model, plus a few seconds per
note). /escutar and the hook stay quiet and say what is missing.
Verification
After the restart, dump the profile config and look for the row:
npx @deepseek-ai/dsh --profile web --dump-config
# == dsh-escutar
- id: escutar
name: dsh-escutar
Then ask the agent to call conferir_transcricao with {"id": "001"} (or run
/escutar when the cache exists). A live measurement returns the cosine, the
verdict and the transcript read from the cache.
From a source checkout, run the test suite:
npm test
The suite covers the verdict band, the CLI output parser, the cache freshness guards, the command, the hook and the missing-binary error. It uses shell stubs, so it runs on Linux and macOS; installing the plugin does not depend on it.
Limits
- The audio encoder reads about 30 seconds. A long note is judged by a window, not by the whole recording.
- A plausible but wrong transcript can pass. Swapping one word for another word on the same subject may not move the cosine below the threshold. The signal catches gross errors: wrong language, noise, text from another note.
- It does not judge the content. It only says whether the audio and the text talk about the same thing in the same semantic space.
License
Comments
Loading…
Similar plugins
by mohith-das
Minimal, English-only voice input plugin for DeepSeek Harness Web UI — a mic button in the composer using the browser's native SpeechRecognition API. Zero dependencies, no subprocess, no tracking.
★ 0
MIT
JavaScript
Sep 1, 2026
dsh plugin --profile web add dsh-voice-input-enby zemanzhang809
A speech-to-text (voice input) plugin for DeepSeek Harness.
★ 1
MIT
TypeScript
Sep 18, 2026
dsh plugin --profile web add dsh-stt-pluginby xxxxxxxyu
DSH (DeepSeek Harness) web plugin: plays a sound when the agent finishes replying (turn/end). Sound, volume and on/off configurable in Settings.
★ 3
↓ 45/wk
MIT
TypeScript
Aug 14, 2026
dsh plugin --profile web add dsh-notify-soundby 3274375092
Voice input plugin for DeepSeek Harness: mic → local/browser speech recognition → text submitted as a normal chat message. Input-only and preset-agnostic.
★ 8
↓ 158/wk
MIT
TypeScript
Oct 1, 2026
dsh plugin --profile web add @nn12138/dsh-voiceby wencharmwang
plugin of voice input for DeepSeek Harness
★ 0
MIT
TypeScript
Aug 18, 2026
dsh plugin --profile web add dsh-voiceby porcelaintech
Voice input plugin for DeepSeek Harness: a minimal mic control in the composer with continuous bilingual recognition, context-aware term correction, punctuation, and a live waveform
★ 3
MIT
TypeScript
Sep 3, 2026
dsh plugin --profile web add @porcelaintech/dsh-plugin-voice-input