feat: no-answer retry, agent status machine, escalation events/timeline (ТЗ §13-15,22,25-27,37, AC-08,16-19)
Phase 2 of the L1->L2 routing engine (Phase 1: MR!4). - ami_loop() now also captures native AMI DialEnd/Hangup frames (not only UserEvent), needed to detect that an escalated agent did not answer. No dialplan change required - Redirect already routes the client channel into an existing Dial()-based transfer context, so Asterisk emits these events on its own; the listener just wasn't reading them before. - retry_escalation_no_answer(): on NOANSWER/BUSY/CANCEL/CHANUNAVAIL/ CONGESTION, releases the non-answering agent, excludes it, and reserves+redirects to the next available agent via the routing engine's existing exclude_agent_ids support. Exhausted pool marks the escalation failed and leaves the call with the AI instead of dropping the client (ТЗ §32). - Agent status now actually moves through RESERVED -> RINGING -> TALKING -> AFTER_CALL_WORK -> AVAILABLE instead of staying stuck on RESERVED for the whole call; a new acw_sweep_loop background thread (same pattern as the existing failed_retry_loop) times out AFTER_CALL_WORK back to AVAILABLE. - escalations gains attempt_count/real_agent_id/attempted_agent_ids_json (migration 0033); fixes a latent bug where assigned_agent_id stored the SIP extension instead of the real agent_id despite routing-service already returning it in RoutingAgentReserveOut. - Every transition now records an interaction timeline entry and publishes the ТЗ §25 event catalog (AgentReserved/AgentRinging/ AgentNoAnswer/AgentConnected/TransferCompleted/TransferFailed) through the existing emit_voice_event/EventOutboxRow idempotent path. Not in this MR (see plan): SLA config, Callback, L3 (needs real technical agents from the business), metrics.
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "AgentConnected",
|
||||
"type": "object",
|
||||
"required": ["event", "call_id", "escalation_id", "agent_id", "created_at"],
|
||||
"properties": {
|
||||
"event": { "const": "AgentConnected" },
|
||||
"call_id": { "type": "string" },
|
||||
"escalation_id": { "type": "string" },
|
||||
"agent_id": { "type": "string" },
|
||||
"created_at": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "AgentNoAnswer",
|
||||
"type": "object",
|
||||
"required": ["event", "call_id", "escalation_id", "agent_id", "dial_outcome", "created_at"],
|
||||
"properties": {
|
||||
"event": { "const": "AgentNoAnswer" },
|
||||
"call_id": { "type": "string" },
|
||||
"escalation_id": { "type": "string" },
|
||||
"agent_id": { "type": ["string", "null"] },
|
||||
"dial_outcome": { "type": "string" },
|
||||
"attempt_count": { "type": "integer" },
|
||||
"created_at": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "AgentReserved",
|
||||
"type": "object",
|
||||
"required": ["event", "call_id", "escalation_id", "agent_id", "created_at"],
|
||||
"properties": {
|
||||
"event": { "const": "AgentReserved" },
|
||||
"call_id": { "type": "string" },
|
||||
"escalation_id": { "type": "string" },
|
||||
"agent_id": { "type": "string" },
|
||||
"tenant_id": { "type": ["string", "null"] },
|
||||
"from_level": { "type": "string" },
|
||||
"to_level": { "type": "string" },
|
||||
"reason_code": { "type": "string" },
|
||||
"attempt_count": { "type": "integer" },
|
||||
"created_at": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "AgentRinging",
|
||||
"type": "object",
|
||||
"required": ["event", "call_id", "escalation_id", "agent_id", "created_at"],
|
||||
"properties": {
|
||||
"event": { "const": "AgentRinging" },
|
||||
"call_id": { "type": "string" },
|
||||
"escalation_id": { "type": "string" },
|
||||
"agent_id": { "type": "string" },
|
||||
"created_at": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "TransferCompleted",
|
||||
"type": "object",
|
||||
"required": ["event", "call_id", "escalation_id", "agent_id", "created_at"],
|
||||
"properties": {
|
||||
"event": { "const": "TransferCompleted" },
|
||||
"call_id": { "type": "string" },
|
||||
"escalation_id": { "type": "string" },
|
||||
"agent_id": { "type": "string" },
|
||||
"created_at": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "TransferFailed",
|
||||
"type": "object",
|
||||
"required": ["event", "call_id", "created_at"],
|
||||
"properties": {
|
||||
"event": { "const": "TransferFailed" },
|
||||
"call_id": { "type": "string" },
|
||||
"escalation_id": { "type": ["string", "null"] },
|
||||
"agent_id": { "type": ["string", "null"] },
|
||||
"reason": { "type": ["string", "null"] },
|
||||
"error": { "type": ["string", "null"] },
|
||||
"created_at": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user