- Python 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| docs | ||
| gocall | ||
| .gitignore | ||
| README.md | ||
| sip-agent.service | ||
sip-agent
Speak and chat through SIP as agent@sip.sad.ovh. A pure Go SIP agent (agentd) manages both the voice plane (diago) and chat MESSAGE plane (sipgo) concurrently. Plays neural synthesized speech (KittenTTS via native ONNX Runtime in Go) and transcribes responses (Groq cloud STT). No Python runtime, no baresip.
Extensions on the server: 101 rings sophie, 102 rings the agent, 600 is the echo test. --to takes a name or a number, so --to sophie and --to 101 reach the same phone, and --to echo runs the echo test.
Documentation suite
Detailed system documentation is available in the docs/ directory:
- Architecture (
docs/ARCHITECTURE.md): System architecture, voice and chat plane concurrency, full-duplex audio separation, and session management. - Daemon & CLI Reference (
docs/DAEMON_AND_CLI.md): Commands, flags, IPC socket protocols, systemd user service setup, and multi-agent routing. - Audio & AI Pipeline (
docs/AUDIO_AI_PIPELINE.md): Native KittenTTS ONNX inference, SoX telephony audio conditioning, Groq STT integration, and fail-closed judging logic. - Security & Integration (
docs/SECURITY_AND_INTEGRATION.md): Global voice permission gate (AGENTS.md), OpenBao secret management, and approval audit logging.
Dial plan config
All of it is handled in gocall/internal/config with env overrides, so no code edits are needed for a new box or a new contact:
SIP_DOMAIN default sip.sad.ovh
SIP_USER default agent
SIP_DEFAULT_TO default 101
SIP_SOPHIE_EXT default 101
SIP_AGENT_EXT default 102
SIP_ECHO_EXT default 600
SIP_EXTRA_EXTS extra names, e.g. "bob=103,alice=104"
So SIP_EXTRA_EXTS="bob=103" ./gocall/bin/gocall --to bob "hi" dials 103, and the pre-dial presence check follows the name.
Quick start
Voice calls
One-off announcement, no answer needed:
./gocall/bin/gocall --to 101 "Hello Sophie, this is the agent"
Ask something and wait for yes or no:
./gocall/bin/gocall --to 101 --listen-yes "May I restart the box? Answer yes or no."
Ask an open question and print the reply as text:
./gocall/bin/gocall --to 101 --ask "Which host is slow right now?"
Check the audio path without bothering anyone:
./gocall/bin/gocall --to 600 "echo check"
SIP Messages (Chat)
Send a SIP MESSAGE instantly:
./gocall/bin/agentd send --to 101 "Hello from the agent"
Run or test the multi-agent SMS-style reply loop:
# Test simulated message against the active agent session
./gocall/bin/agentd loop --test "What are you working on right now?"
How a call runs
- Secret retrieval is native Go via OpenBao AppRole login (
gocall/internal/secret). The SIP password lives in memory only. - The message is rendered to an 8 kHz mono wav by the native KittenTTS engine, with 4 s of lead silence and the text repeated twice.
gocalldials viaagentd(or registers on demand in one-shot mode). The wav plays as the mic while an RX-only tap records decoded peer audio to monorec.wav. Our own TX never enters the recording, so the negation veto cannot fire on our question.- For
--to 600the script checks the recording level. RMS above -60 dB means audio returned through Echo, confirming both TX and RX.
Voice & KittenTTS
Default voice is Jasper from KittenTTS Mini 0.8.
The model is evaluated natively via ONNX Runtime inside gocall/bin/tts-daemon behind a unix socket (tts.sock), completely free of Python dependencies. Rendered lines are cached on disk under ~/.cache/sip-agent/tts/ by hash of text plus voice.
Flags:
--tts-engine espeak: skips the neural voice and usesespeak-ng.--voice <name>: selects an alternate Kitten voice (e.g.Jasper,Bruno).
Answers and approvals
--listen-yes loops the question for --listen-window seconds (default 50) and polls the tail of the recording through Groq whisper-large-v3-turbo. The API key lives in .groq_api_key, mode 0600.
The rule is fail closed:
- Silence, timeout, or remote hangup without an explicit yes counts as denied.
- A yes requires two consecutive positive detections to filter out echo or line blips.
- Any negative keyword (
no,nope,nah,stop,don't, etc.) in the transcript vetoes approval. - Every granted approval persists its audio and transcript under
approvals/.
gocall --ask is the open-ended form. It plays the question once, holds the call until hangup or --timeout expires, and prints ANSWER: <transcript>.
Unified Agent Daemon (agentd)
gocall/bin/agentd manages both the voice plane and the SIP MESSAGE plane in a single process:
agentd start|stop|status|restart: controls the persistent daemon holdingagentd.sock.agentd send --to <target> "<message>": sends an out-of-dialog SIP MESSAGE with digest auth and automatic UDP MTU chunking.agentd loop: watchesinbox/messages.log, dynamically auto-detects the active agent session (Antigravity or OpenCode) by modification timestamp, prompts the session, and replies over SIP MESSAGE.- Background integration:
agentd serveautomatically runs the reply loop in the background under systemd (sip-agent.service).
Repository layout
| Path | What it is |
|---|---|
docs/ |
Comprehensive technical documentation suite |
gocall/cmd/agentd/ |
Unified daemon: voice job socket + SIP MESSAGE receive/send/loop |
gocall/cmd/gocall/ |
Dial CLI: --listen-yes confirm, --ask Q&A, --to 600 echo test |
gocall/cmd/tts-daemon/ |
Native Go KittenTTS ONNX inference daemon |
gocall/internal/call/ |
SIP call dialog management and RTP playback/recording |
gocall/internal/kitten/ |
ONNX KittenTTS tokenizer and inference wrapper |
gocall/internal/msg/ |
SIP MESSAGE receive, chunking, and send engine |
gocall/internal/secret/ |
OpenBao AppRole client (memory-only password fetch) |
gocall/internal/stt/ |
Groq Whisper API client |
gocall/internal/tts/ |
TTS orchestration, disk caching, and sox assembly |
sip-agent.service |
Systemd user unit (symlinked to ~/.config/systemd/user/) |
approvals/ |
Stored audio and transcripts for approved voice votes |
inbox/ |
Received chat messages (inbox/messages.log), runtime only |
spool/ |
Temporary call wavs |
Requirements
- Go 1.22+
sox,espeak-ng- ONNX Runtime library (
libonnxruntime.so) - Groq API key in
.groq_api_key - OpenBao AppRole credentials