Atlas / Skills / voicehook-ai / voicehook-agent

voicehook-agentCAUTION

skills/voicehook-ai/voicehook-agent

Talk with your agents. Zero-install CLI that lets any coding agent (Claude Code, Codex, opencode) join a voicehook.ai voice call. stdin/stdout protocol, no SDK.

Verdict
CAUTION
Grade
C
Trust score
71 /100
Version
—
Hosts
4 documented
License
MIT
Stars
0
01

Overview

From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.

[](https://github.com/voicehook-ai/voicehook-agent/actions/workflows/ci.yml) [](https://github.com/voicehook-ai/voicehook-agent/actions/workflows/codeql.yml) [](LICENSE) [](plugins/voicehook-join/.claude-plugin/plugin.json) [](pyproject.toml)

Talk to your coding agents instead of reading their output.

voicehook.ai is a voice call in the browser. Hit "invite agent", hand the link to Claude Code, Codex or opencode, and talk to it while it keeps working. Delta, the voice in the call, answers from the status board and FAQ your agent keeps up to date, so you hear what is going on without opening a terminal.

This repo holds the agent side: the voicehook-join Claude Code plugin and the voicehook-agent CLI that lets any agent with a shell join a call.

Three ways to connect

Read from source at commit e85044119258OBSERVED · 2026-10-10
02

Install

Commands as the repository documents them. They are shown, not run.

uvx voicehook-agent join 'https://voicehook.ai/r/<slug>?invite=<code>' --name <your-own-name> --model <your-model-name>
uvx voicehook-agent join 'https://voicehook.ai/r/<slug>?invite=<code>' --name <your-own-name> --model <your-model-name>
uvx voicehook-agent join 'https://voicehook.ai/r/<slug>?invite=<code>' --name <your-own-name> --model <your-model-name>
uv tool install voicehook-agent
uv tool install -q --upgrade $R || pip install -q --upgrade --user $R
uv tool upgrade voicehook-agent
03

Host compatibility

What the documentation claims. We have not run a compatibility test.

HostStatusNotes
claude-codementioned
claude-desktopmentioned
codexmentioned
cursormentioned
04

What it tells the agent

The instruction file, verbatim from the audited commit — this is the text the model reads, and the surface the audit's instruction layer examines. Quoted here so you can judge it without cloning anything.

---
name: voicehook-join
description: Join or start a voicehook.ai voice call as the brain behind its voicebot, through the voicehook MCP tools (create_call, join, next, say, status, activity, show, leave). Use when the user shares a voicehook invite URL (https://voicehook.ai/r/<slug>?invite=...), says "join voicehook", "tritt meinem Call bei", "übernimm den voice-call", "/voicehook-join", or wants you to call them ("ruf mich an", "call me", "lass uns telefonieren"). The voicebot speaks what you send with `say`; you listen with `next`.
---

# voicehook-join (MCP): be the brain in a voicehook call

Canonical: https://voicehook.ai/agent/mcp/SKILL.md. You only use the tools of the
`voicehook` MCP server. Nothing to install, no keys: the invite link is the permission.

## Hard rules (read first, always)

- **`create_call`: the link first.** FIRST give your user share_text, verbatim.
  - Claude Code / clients that can keep a background process running: start join + next in the background, then END your turn with share_text as your final message; intermediate text may never reach a remote user.
  - claude.ai chat, remote apps / clients that only show your final message: END YOUR TURN with share_text as your final message and call join when the user replies that they opened it.
  - join waits up to 50 s and returns status waiting_for_human until they open it; then call join again at once.
  - Until they have joined, end EVERY message to your user with share_text as the last block. A link further up scrolls away.
  - Never wait or block in the foreground before the link is out as your final message, never "I am waiting for you" in the foreground. If your channel can, also send the link as a file or push. On 5xx, 503 or a network error call the tool again after 2 s.
- **Right after `join`, at once** (also with `quiet: true`: quiet only suppresses `share` and `notice`, nothing audible). You lead: never wait silently until the human says something.
  1. Greet: `greet` (true by default in `join`) speaks the default greeting in the call language. With greet, the `say` in `context.first_actions` drops out. Without greet: right after join, `say` one short greeting in the call language: who you are and that you are listening ("Hi, this is <your label>, I'm in and listening. What's up?" / "Hallo, <your label> hier, ich bin drin und höre zu. Worum geht's?"). If you stay silent, the voicebot announces you after 3 seconds.
  2. `status` with `doing`, `open`, `done`.
  3. One `activity` line.
  4. Open questions for the human: `decide` (Decision Board), never into `open`.
  5. `show` with `panel: "board"` (or `log`, `decisions`) so the human sees it at once.
- **Acknowledge within 3 s:** on every request of the human first one short `say` ("On it, one moment." / "Mach ich, Moment."), THEN work. Progress goes to `status` and `activity`, never silence.

## Join

1. Call `join` with `invite_url` = the FULL invite link the user gave you (including
   `?invite=`), `label` = the text on your chip in the call (max 22 characters: your session
   or chat name, else your model name; too long is rejected), `model` = the exact model id
   you run on (not shown). Never call yourself "Claude" if you are not Claude. Unknown model:
   `"unbekannt"`. The old `name` still works as `label`. `greet` is true by default (`false` = you greet with `say`).
2. Keep the returned `session` and pass it as `session` to EVERY other tool. It is a short-lived handle
   for this one call; it ends with the call, after 10 min without `next`/`say`, or with `leave`. Never show it in the call or to anyone else.
3. `lang` = the call language: speak it. `share` = one line for YOUR user (show it once
   in your chat, never say it in the call); `quiet: true` drops it (and `notice`; nothing audible). `context` = lang, mode,
   instructions, functions, rules, skill_url: the source of truth for this call.
4. Then the Hard rules checklist (greeting, board, feed, decide, panel) and `next` at once.

## Or call your user yourself (`create_call`)

1. `create_call`: set `lang` to the language of the person you are calling (`de` or `en`, default `de`).
   You get `human_url`, `share_text`, `operator_invite_url`, `room` and `expires_in` (seconds; an unopened link expires, 30 min).
2. Give your user `share_text` verbatim (it holds `human_url` and the hint to use a normal
   browser; an app's built-in browser blocks the mic) as your FINAL message (Hard rules). The opener
   pays from THEIR free allowance or credit, you pay nothing. Never send `operator_invite_url` to anyone.
3. `join` with `invite_url` = `operator_invite_url` (in the background, or when your user says
   the link is open). It waits up to 50 s and returns status `waiting_for_human` (not an error,
   with `share_text` again) until the person opens the link: call `join` again at once, until it
   succeeds or answers `410` (link expired: ask your user whether to start a new call).
4. Then the Hard rules checklist and `next` as below.

## The work cycle: say -> next -> say

`next` (waits up to 50 s for the user) -> one `say` -> `next` again at once. Never sleep, never poll, never end your turn while the call runs. Working? Still call `next` at least every 10-15 s (split long work into steps, `next` in between). Exactly ONE `next` loop per agent: a second one makes the older answer `superseded`. After a pause `next` brings `missed` (all user turns you missed, oldest first) and `away_s`: answer them together in one `say`.

| `type` | meaning | do |
|---|---|---|
| `user` | `text` = what the user just said | answer with ONE `say` |
| `revise` | your queued says were replaced; `reason: "user_stop"` = the user said "stopp"/"reicht" | still needed: one merged `say` with `mode: "overwrite"`; `user_stop`: never resend, wait |
| `status_request` | the user asked what you are doing | send a `status` board at once |
| `timeout` | silence (also `reconnect: true` = server restart) | call `next` again |
| `human_le
05

Trust audit

CAUTIONgrade C · trust 71/100 Install with care. The audit found things worth knowing before you trust its output.

LayerWhat it checksResult
L0Provenance & inventoryPASS
L1Static analysis of the codeWARN
L2Instruction surface (what it tells the agent)PASS
L3Class-specific surfacePASS
L4Behavioural (sandbox)SKIPPED

What the source does

Filesystem
none-observed
Network
declared (8 observation(s))
Shell
none-observed
Dependencies
pinned
Secrets in source
found

Findings (12)

MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
src/voicehook_agent/cli.py:2415
p_sum.add_argument("--ollama-url", default="http://127.0.0.1:11434",
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
src/voicehook_agent/cli.py:2416
help="Ollama base URL (default http://127.0.0.1:11434).")
MEDIUMHard-coded secrets · secret.anthropic · CWE-798, CWE-321
tests/test_activity.py:25
"sk-ant-api03-abcdefghijklmnop",
MEDIUMHard-coded secrets · secret.github · CWE-798, CWE-321
tests/test_activity.py:30
"ghp_abcdefghijklmnopqrstuvwxyz0123456789",
MEDIUMHard-coded secrets · secret.github · CWE-798, CWE-321
tests/test_activity.py:139
activity.post_tool_use(_event(tool_input={"description": "Key ghp_abcdefghijklmnopqrstuvwxyz01234 testen"}))
MEDIUMHard-coded secrets · secret.github · CWE-798, CWE-321
tests/test_activity_due.py:130
"file_path": "/tmp/ghp_abcdefghijklmnopqrstuvwxyz0123456789.txt"}))
MEDIUMHard-coded secrets · secret.slack · CWE-798, CWE-321
tests/test_activity.py:32
"xoxb-123456789012-abcdefghij",
MEDIUMHard-coded secrets · secret.stripe · CWE-798, CWE-321
tests/test_activity.py:24
"sk_live_51Habcdefghijklmnop",
LOWInventory / provenance · inv.hidden_file · CWE-1104
.gitleaks.toml
.gitleaks.toml
Why it matters. hidden member outside the usual dotfiles
Fix. review its purpose
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
tests/test_activity_auto.py:145
activity.save_bridge_session({"session": "s"}, "http://127.0.0.1:9")
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
tests/test_activity_auto.py:148
assert activity._bridge_post("http://127.0.0.1:9", "s", "x") is None
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
tests/test_activity_auto.py:171
activity.save_bridge_session({"session": "s"}, "http://127.0.0.1:9")

Gates applied: no_behavioural_pass.

Audited 2026-10-10 · audit v0.4.1 · source sha e85044119258full audit observations/trust-audit/skill/voicehook-ai__voicehook-agent.json · Report an issue / request a re-scan
06

Audit history

Every audit this skill has had.

DateSourceVerdictGradeScoreChange
2026-10-10e85044119258CAUTIONC71first audit
07

Questions

What does the voicehook-agent skill do?

Talk with your agents. Zero-install CLI that lets any coding agent (Claude Code, Codex, opencode) join a voicehook.ai voice call. stdin/stdout protocol, no SDK.

Is voicehook-agent safe to install?

With care. The audit graded it C (71/100) and found 12 things worth knowing before you trust this skill, listed below with the exact line each was found on.

What can voicehook-agent access on my machine?

The audit observed that it reaches the network. Each of those is consistent with what it says it does. Secrets in the source: found — see the findings.

Which assistants does voicehook-agent work with?

Its documentation mentions claude-code, claude-desktop, codex and cursor. That is what the text claims, not a compatibility test we ran.

How current is this page?

The grade is for one exact copy of the source (e85044119258), read on 2026-10-10. The repository is watched, and a new audit runs when it changes — this is the first audit.

Advertisement