What counts as an active call
telephony.listActiveCalls reads a per-org sorted set at
telequick:<orgId>:active_calls, scored by call start time. A call
row appears on the screen only if it survives every one of the following
checks.
- Recency cutoff. The query reads the index with
ZREVRANGEBYSCOREfrom+infdown tonow - 60 minutes. Anything scored older than that is never considered. Real calls finish in seconds to minutes; an entry older than the cutoff is almost certainly a stale index row left behind by a hangup path that never removed it — a carrier disconnect, a gateway restart mid-call, or a Redis blip. - Row limit.
limitdefaults to100and is capped at500. The limit is applied both to the initial index read and to the final sorted result. - End-record gate. For each candidate SID the procedure checks whether
session:<sid>:endexists.agent_runtimewrites that record on hangup (the RTP-listener silence-finalize / dialog-closure path). If the record exists, the call is over — even ifrelease()failed to remove the SID from the index — and the call is treated as ended. - Session hash present and tenant-matched. For each surviving SID the
procedure reads
tenant_id,agent_id,trunk_id,realm,started_at_ms, andnode_idfromsession:<sid>. A row whose read errored, whosetenant_idis missing, or whosetenant_iddoes not equal the requestingorgIdis dropped. - Start time re-check.
started_at_msmust parse to a finite number that is not older than the same 60-minute cutoff.
limit.
Self-healing eviction
Candidates that failed the end-record gate are removed from the sorted set inline, as a best-effort pipelinedZREM. The cleanup is fire-and-forget: if it
fails, those SIDs are simply filtered out again on the next listing, so the
index converges without an external reaper.
Fields on each row
The duration shown in the row is computed client-side from
started_at_ms, so
it keeps ticking between the 8-second refreshes.
Supervisor modes: listen, whisper, barge-in
Clicking Supervise opens the supervise panel for thatcall_sid. The
operator picks one of three modes. The console sends the mode to the control
plane, which dispatches a Supervise op onto the per-tenant rpc_bridge queue;
the gateway then records the supervisor as a participant on that session’s room.
Choosing the whisper target
Whisper mode requires awhisper_target naming which leg hears the supervisor:
agent— only the agent leg hears the supervisor.caller— only the caller leg hears the supervisor.
whisper_target. The requirement is enforced in both
places: the control-plane procedure rejects a whisper request without a target,
and the C++ rpc_bridge handler validates the same condition, so a request
crafted against the gateway directly cannot bypass it.
Minting a supervisor token and the required scope
Supervision is not covered by an ordinary session credential. The console mints a short-lived, opaque supervisor token and hands it back to the gateway inSuperviseSubscribeRequest.auth_token.
The gateway resolves the token at sa:<token> in Redis and admits the subscribe
only if both conditions hold:
- the token’s
scopescontaintelephony:supervise, and - the token’s
tenant_idmatches the tenant of the call being supervised.
The regular agent role is deliberately not granted
telephony:supervise.
Supervising moves live call audio outside the per-call participant set, so it is
modelled as an explicitly elevated action rather than something any agent
identity picks up implicitly.
What works today and what is pending
The supervisor path ships in stages, and the Active calls screen is honest about which stage you are in. Working today- The active-call listing, liveness gating, and inline eviction described above.
- Token minting with the role gate,
call_sidbinding, and 10-minute TTL. - Gateway admission:
sa:<token>resolution, scope check, and tenant check. - Dispatch of the
Superviseop onto the tenant’srpc_bridgequeue, and registration of the supervisor as a participant on the session room. - Mode and
whisper_targetvalidation, in the control plane and again in the C++ handler.
- Audio routing is a no-op. Until the
codec_enginemixer follow-up lands, joining as a supervisor does not move audio in either direction, for any of the three modes. The call returnsSUPERVISOR_JOINEDorSUPERVISOR_UPDATEDso the console can confirm that the intent was accepted and render the pending state. Treat the Active calls screen as a read-only monitor for now.
Live engine telemetry ticker
The lower panel on the screen renders recent engine telemetry frommonitoring.agentEvents, which reads an in-process ring buffer rather than the
call index. The procedure takes a limit (default 50, maximum 200; the
screen requests 40) and returns an enabled flag alongside the events. When
the telemetry consumer is not enabled, the event list comes back empty.
Each rendered line shows the event’s receive timestamp, its channel, and a
flattened summary of the first few fields of the raw payload. tenant_id,
org_id, ts, and timestamp are omitted from that summary because they are
either redundant with the row context or already displayed. Payloads that do not
parse as JSON are shown truncated.
Pause stops the 3-second refresh entirely; the last fetched batch stays on
screen until you resume.
Troubleshooting an empty or stale list
The list is empty but calls are in progress.- Confirm the session hash’s
tenant_idmatches the org you are viewing. Rows whosetenant_iddiffers from the requestedorgIdare filtered out silently. - Confirm the call was added to
telequick:<orgId>:active_callswith a start-time score. A call absent from the index is invisible to this screen regardless of its real state. - Check for a premature
session:<sid>:endrecord. Its presence is treated as proof the call ended, and the SID is evicted from the index on the spot.
- It will disappear on the next listing once
session:<sid>:endis written, or once its start time passes the 60-minute cutoff. Eviction happens during a listing, so the self-heal requires at least one more poll. - Eviction is best-effort. If the cleanup pipeline fails, the entry remains in the index but stays filtered out of the response.
- Anything scored older than 60 minutes is outside the window and will not be listed, even if the call is genuinely still up. The 60-minute ceiling replaced a much wider window that surfaced hours-old hung-up calls to operators.
- The default
limitis 100 and the maximum is 500. The limit bounds both the index read and the final result, so a very busy org sees the most recently started calls first.
- Expected. Audio routing is pending the mixer work; see What works today and what is pending.
- The minting identity must be an org owner or admin.
- The token must carry
telephony:superviseand a matchingtenant_id. - The token is bound to one
call_sidand lives 10 minutes; a token minted for a different call, or an expired one, is refused at the gateway.
Related
- Telemetry — metrics, traces, CDRs, and SIP capture
- Authentication — API keys, scopes, and short-lived tokens
- Telephony Metrics — definitions for the numbers on live calls