Voice request lifecycle¶
Status: Current | Last reviewed: 2026-05-28
What happens when a phone call hits TrueServe. Read this before universal pipeline builder.
The path¶
Caller / Carrier
│ SIP INVITE
▼
kamailio (SIP routing, TLS termination)
│ SIP INVITE rewritten
▼
asterisk (call control via PJSIP realtime)
│ Stasis app handoff via ARI
▼
ari-controller-node (decides what to do; binds the channel to media)
│ Bridge channel to external media
▼
media-proxy (Go) (decodes RTP, mixes, ducks; resamples)
│ bidirectional gRPC audio
▼
ai-orchestrator (Python) (Pipecat pipeline built from agent_versions.config)
│ STT → LLM → TTS, plus tools
▼
agent-host (MCP tools) ←→ external integrations (Salesforce, knowledge-engine, …)
The reverse path carries TTS audio back: ai-orchestrator → media-proxy → asterisk → kamailio → caller.
Per-hop responsibility¶
kamailio¶
Front-door SIP proxy. Handles TLS, registration, dial-plan rewriting, ACL. In Sentinel and city dev environments, kamailio is the only service exposed publicly on SIP ports. It forwards trusted calls to asterisk over UDP/TCP on the loopback (Vanguard) or pod network (cluster).
Config lives in charts/kamailio/ for cluster deploys and .devcontainer/configs/kamailio/ for Vanguard. Per-city TLS certs are baked into the container image during city rollout; see city-dev-environments.md.
asterisk¶
PBX with PJSIP realtime backed by the ps_* tables in Postgres. Asterisk is configured as a B2BUA: it accepts a leg from kamailio and dials a Stasis application that hands control to ari-controller-node over WebSocket on :8087.
The dialplan does very little logic. Everything beyond "answer and hand off" is in ari-controller.
ari-controller-node¶
A Node.js service that holds an ARI WebSocket open to asterisk. When a Stasis event arrives, it:
- Resolves the agent for this call (from dialed number → tenant → agent).
- Allocates a media-proxy session.
- Bridges the asterisk channel to media-proxy's external media (RTP).
- Tells
ai-orchestratorto start a pipeline for this conversation.
For warm hand-off, ari-controller drives the consult: it spins up a second outbound channel, mixes the agents into a bridge, then unbridges on completion. See warm-handoff.md.
media-proxy¶
Go service that takes raw RTP from asterisk on one side and bidirectional gRPC audio frames from ai-orchestrator on the other. Handles:
- Codec negotiation (G.711 µ-law, A-law, Opus).
- Resampling between carrier rate (8 kHz / 16 kHz) and pipeline rate (24 kHz).
- Mixing and ducking when multiple streams are active (translation TTS, underbed, hold music).
See audio-ducking.md.
ai-orchestrator¶
Python service running Pipecat. For each call it:
- Resolves the agent's config via
resolve_agent_version()(sql-functions.md). - Builds the pipeline through universal_builder.py, which selects a subtype builder (linear, S2S, translation, parallel-agent, etc.).
- Runs the pipeline: audio in → STT → LLM → tool calls (via agent-host) → TTS → audio out.
- Persists transcripts and events through
conversation-api.
agent-host¶
MCP tool server that exposes the agent's tools (CRM lookups, knowledge search, custom Python, A2UI components) over the Model Context Protocol. ai-orchestrator dials agent-host as an MCP client; the LLM emits tool calls that flow back through.
agent-host runs the MCP streamable endpoint on :8003. See agent-host.md.
What carries the conversation state¶
| Layer | Where state lives | Lifetime |
|---|---|---|
| SIP signaling | asterisk channels | Call duration |
| Media | media-proxy in-memory session | Call duration |
| Pipeline | ai-orchestrator process memory | Call duration |
| Transcript | conversations + conversation_messages (Postgres) |
Forever |
| Recording | conversation_recordings row + S3/GCS/MinIO blob |
Forever |
| Tool state | agent-host worker, optionally in Redis for cross-pod consistency | Tool-defined |
If ai-orchestrator dies mid-call, the call drops. Persistent state is in the conversation tables; the live pipeline is intentionally ephemeral.
Where to look when it breaks¶
| Symptom | Read |
|---|---|
| No audio | troubleshooting-voice.md, audio-ducking.md |
| Caller hears silence after greet | media-proxy logs; check ai-orchestrator pipeline state |
| Translation cuts in/out | troubleshooting-translation.md |
| Transfer fails | warm-handoff.md |
| 502 on register | vanguard-nginx-ingress — Janus WS header strip |