trunk table for the
static binding, telephony.listActiveCalls for live per-trunk session
counts, and monitoring.agentEvents for the event tail.
Agent orchestration itself runs in the native C++/Seastar agent_runtime
binary. The console does not drive dispatch; it reports on it.
What the orchestrator screen shows
The screen has three regions, top to bottom:
The four KPI tiles are derived purely from trunk configuration, not from
live traffic:
- Configured Trunks — the number of
trunkrows for the active org. - Total Channel Cap — the sum of
channel_limitacross those rows. - Trunks w/ Agent — the count of rows with a non-null
agent_id. - Avg Channels/Trunk — total channel cap divided by trunk count, rounded.
How a trunk is bound to an agent
A binding is a single column. Eachtrunk row carries an agent_id; if
it is set, inbound calls arriving on that trunk are dispatched to that
agent by the runtime. If it is null, the table renders none in the
Agent column and the trunk contributes nothing to the Trunks w/ Agent
tile.
The table renders these fields per trunk:
Because the binding is per trunk rather than per call, an agent can be
bound to several trunks at once, and a trunk is bound to at most one
agent.
If the org has no trunks at all, the table is replaced with an empty
state prompting you to add a trunk and bind it to an agent.
Reading a live agent event line
The Live Agent Events panel pollsmonitoring.agentEvents for the
per-tenant ring of telemetry records the agent runtime publishes on its
telequick.agent.* topics. Polling is used deliberately rather
than a push transport: the control-plane API stays stateless from the
browser’s point of view, and closing the tab simply stops the next poll.
Each row is laid out in fixed columns:
The payload column hides
session_id and tenant_id, because the
session is already shown in its own column and the tenant is implied by
the org you are viewing. Everything else in the record is rendered as-is.
Newest events render at the top. The panel buffers up to the query’s
limit (the screen requests 100) and reports the buffered count in the
header. Pause stops the 2 s refresh so a line does not scroll away
while you read it; Resume restarts it. Polling also stops while the
tab is in the background.
monitoring.agentEvents returns an enabled flag alongside the events.
When the telemetry consumer is not enabled, the procedure returns an
empty event list. An empty panel with a live broker shows a prompt to
run Test Drive on an agent to produce a first event.
How per-trunk session counts are derived
The Sessions column is not reported by the runtime. The screen callstelephony.listActiveCalls for the org every 10 seconds — the same
cadence the dashboard’s active-calls view uses — and groups the returned
rows by their trunk_id client-side. A trunk with at least one matching
row renders a pulsing dot and N live, suffixed with / channel_limit
when a channel limit is configured. A trunk with no matching rows renders
a muted dot and 0 live.
telephony.listActiveCalls builds its result like this:
- Read call SIDs from the per-org active-calls sorted set,
telequick:<orgId>:active_calls, highest score first, bounded by thelimitinput. - For each candidate SID, check whether the runtime has written a
session:<sid>:endhash. The runtime writes that key on hangup, via the RTP listener’s silence-finalize path or dialog closure. Its presence means the call is over. - SIDs whose
:endkey exists are treated as stale, removed from the listing, and evicted from the sorted set inline. Eviction is best-effort; if it fails, the same entries are filtered again on the next listing. - For the surviving SIDs, read
tenant_id,agent_id,trunk_id,realm,started_at_ms, andnode_idfrom thesession:<sid>hash. - Drop any row whose
tenant_iddoes not match the requested org, and any row whosestarted_at_msis missing, unparseable, or older than the window cutoff. - Sort by
started_at_msdescending and truncate tolimit.
trunk_id are skipped when grouping, so they
contribute to no trunk’s count. realm defaults to internal and
agent_id, trunk_id, and node_id default to empty strings when the
hash field is absent.
Two consequences are worth internalising. First, the count is of sessions
the runtime still considers live, which can differ from what a carrier
believes is up — a carrier-side disconnect that never reached the runtime
leaves a session listed until its :end key appears or it ages out of
the window. Second, the limit is a ceiling on the listing, so on a very
busy org the per-trunk counts reflect only the most recently started
calls that fit under it.
Stale session entries and the 60-minute active window
telephony.listActiveCalls treats “active” as “started within the last
60 minutes”. Anything older is excluded from the sorted-set read and
excluded again by the started_at_ms check after the hash fetch.
The window exists because real calls finish in seconds to minutes. An
entry older than the window is almost certainly a sorted-set row that a
hangup path failed to remove — a carrier disconnect, a gateway restart
mid-call, or a Redis blip. The previous 8-hour window was a holdover from
an earlier SCAN-based implementation and surfaced hours-old hung-up calls
in the operator UI.
Stale entries are therefore handled twice over: the :end liveness gate
catches calls that ended cleanly but were never removed from the index,
and the window catches everything else. Both paths self-heal — the
:end gate evicts what it finds, and the window simply stops reading
old entries.
If a trunk shows 0 live while you believe a call is up, check whether
the call started more than 60 minutes ago, and whether a
session:<sid>:end hash exists for it.
Metrics not yet exposed by the runtime
Theagent_runtime binary does not currently publish live runtime
gauges. Specifically, these are absent, not zero:
- Active session counts as reported by the runtime itself. The orchestrator derives session counts from the active-calls index instead, as described above.
- Reactor stalls.
- Per-trunk throughput.
Related
- Telemetry — gateway metrics, traces, and CDRs
- Telephony Metrics — definitions for channel caps, concurrency, and trunk utilisation