Skip to content

Live copilot: suggestions for the agent during a call ​

The live copilot listens to the human leg of a call and shows the agent short, dismissible suggestions while the call is running. This page is the integrator's view: who is eligible, what the agent receives, what it costs and what it never does. It exists in core from v0.107.0 (core#1975, ADR #264).

Spanish version: Copiloto en vivo.

The vendored openapi/dialer-v1.yaml is 0.106.0 and does not describe the copilot: the events are WebSocket frames delivered to the agent widget. The facts below cite core at tag v0.107.0 (API.md, ADR.md, CHANGELOG.md).

Who is eligible ​

All of these must hold, or the copilot does not open and nothing is spent (Dialer.Copilot.Eligibility.check/1, ADR #264 point 1):

ConditionDetail
PlanTier enterprise only (:copilot in Dialer.PostCall.Entitlement). Any other tier does not have it.
RecordingThe call's recording is pending or available.
ConsentThe recording's consent_basis is CONSENT_STATUS_GRANTED, as frozen when the recording opened. An inbound recording whose consent_basis is null is not eligible in this version (strict reading while the legal advisor's criterion is open).
CallNot a call handled by the AI voice agent, and a known human agent.

It opens when the call becomes bridged. If the call moves to another agent, the session for the previous agent closes (copilot.ended with reason: "agent_changed") and a new one opens for the new agent if that agent is eligible; if core cannot tell who is on the call, it suggests to nobody.

What the agent receives ​

Three WebSocket frames, delivered only to the ddw_ widget socket(s) of the agent on that call, through a private per-agent topic. No dd_ or ddu_ socket, no other agent and not the tenant topic ever receive them (the tenant topic rejects copilot.*, as it does screen.*):

FramePayload
copilot.started{call_id, session_id}
copilot.suggestion{call_id, session_id, suggestion_id, suggestions: [{kind, text}]}: 1 to 3 items; kind is talking_point, objection_reply, compliance_note or next_step; text is 1 to 240 Unicode code points
copilot.ended{call_id, session_id, reason}; reason is budget, call_ended, max_duration, requested, agent_changed, fork_lost (the voice service closed the audio fork) or fork_failed (the fork could not be confirmed open; this one can arrive without a prior copilot.started). Treat any other value as the end of the session

A suggestion informs and executes nothing: the dashboard widget (apps/web/src/widget/CopilotPanel.tsx, present in dashboard v0.51.0 and v0.51.1) lets the agent read it or dismiss it, and there is no other action. The text derives from what the consumer said, so it is untrusted data: render it as text, never as HTML. A suggestion on a do-not-call class is shown, never preselected. Neither the audio nor the suggestion text is written to logs or metrics.

The spend brake (fail-closed) ​

The copilot spends continuously, so it reserves in 60 s tranches under the same spend brake as transcription and analysis (function: "copilot"). The next tranche is reserved before the current one is settled. If the brake denies it (no monthly ceiling, no price, a ledger that cannot be read, a provider change between tranches), the session closes the audio fork and emits copilot.ended with reason: "budget". The call is never cut, transferred, muted or moved: only the suggestions stop. A session also ends by itself after 3600 s (max_duration). A tenant with no monthly ceiling is denied at the first reservation. The brake's rules are in Post-call intelligence.

What this page does not claim ​

  • Anything about what the legal advisor will finally require for inbound consent; the strict reading above is what core does today.
  • The copilot sees only the human leg: there is no stored transcript of the session, so what was not suggested cannot be reconstructed afterwards (ADR #264, limits).

Nothing in these docs is legal advice — always confirm compliance posture with your own counsel.