Files
B2BCall-dialer/docs/QUEUES.md
Matheus a05104a05f feat(agents): agentes, tiers e pausas — call center completo
Fecha agente.md secao 45-49/52. Depois desta fase, um usuario autenticado
consegue logar como agente, entrar numa fila real, se pausar e voltar,
tudo refletido de verdade no FreeSWITCH.

Schema (migration 20260828124245_agents):
- agents (tenant-scoped, RLS): User -> Extension -> identidade de agente,
  state (enum AgentState de 8 valores) espelhando o estado real, so
  alterado via login/logout/pause/resume, nunca escrito direto pela API.
- tiers: Queue<->Agent (level/position 1:1 com mod_callcenter).
- agent_sessions: um ciclo login->logout por linha.
- agent_state_events: historico de transicoes de estado.
- pause_reasons / agent_pause_events (secao 48).

Dois bugs reais corrigidos em FreeSwitchTelephonyProvider, presentes desde
a fase de Event Socket original:
- "queue add/del member" nao existe no mod_callcenter — membership de
  fila usa tier add/tier del. So foi pego agora ao confirmar de novo a
  sintaxe via `help callcenter_config` antes de codar esta fase.
- addAgent/removeAgent nao existiam ainda (agent add/del).

Mecanismo de sync: agentes e tiers nao tem representacao em XML, so
comando ESL direto — diferente do padrao "regenera todos os arquivos"
usado em Trunks/Queues. apps/api publica uma mensagem por acao com
payload (b2bcall:agents:sync, b2bcall:tiers:sync); b2bcall-fs-config
aplica o comando correspondente (agent-sync.ts).

Achados confirmados manualmente contra o FreeSWITCH real antes de codar:
- `agent add`/`tier add` nao sao idempotentes (erro em duplicata) — sync
  ignora esse erro (.catch), condicao esperada em resync.
- `agent del`/`tier del` em algo inexistente nao da erro — seguro chamar
  sem checar existencia antes.
- `agent set status` so aceita 3 valores exatos (Available/On Break/
  Logged Out) — testado deliberadamente com valor invalido.
- Corrida real: atribuir tier antes do primeiro login do agente falha
  silenciosamente do lado do FreeSWITCH (agente so existe la a partir do
  `agent add` no login). Login sempre re-sincroniza todos os tiers do
  agente depois de garantir que ele existe — auto-correcao confirmada no
  teste ponta a ponta.

apps/api: AgentsController (CRUD), AgentsMeController (login/logout/
pause/resume — sempre resolve o agente via JWT, nunca um agentId
arbitrario do client), PauseReasonsController (CRUD), QueueAgentsController
(POST/DELETE de tier em /queues/:id/agents).

Verificado ponta a ponta via curl + fs_cli contra o FreeSWITCH real:
login -> Available, pause -> On Break, resume -> Available, logout ->
Logged Out, todos batendo entre Agent.state (banco) e `agent list`
(FreeSWITCH). typecheck do workspace inteiro limpo. ~350MB de memoria
total (docker stats).

Documentado em docs/AGENTS.md, incluindo lacuna conhecida: estados
derivados de chamada (RINGING/IN_CALL/WRAP_UP/RESERVED) dependem do
evento CUSTOM callcenter::info, ainda nao comprovado chegando em
fs-events nesta sessao (mesma lacuna de sofia::gateway_state ja
documentada em docs/TRUNKS.md) — precisa de uma chamada real passando
pela fila pra investigar.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X1HxY46WGU4G1zmVDNKcWw
2026-08-28 09:58:45 -03:00

3.6 KiB

Filas (mod_callcenter)

Agente.md secao 37 (mod_callcenter como ACD) e 50-51 (Filas/Estratégias). Primeira peça do Call Center — Agentes/Tiers/Pausas ficam pra próxima fase (dependem do fluxo de login do agente, seção 45-49, escopo maior).

Descoberta real sobre callcenter_config

Antes de escrever qualquer código, rodei help callcenter_config no FreeSWITCH real pra ver a sintaxe exata — e ela é bem diferente do que os Trunks fizeram supor:

  • Filas: só queue load/unload/reload/listnão existe queue add nem queue set param. Filas só podem vir de XML estático, carregado/recarregado por nome.
  • Agentes: agent add/del/set status/set state/... — 100% dinâmico via comando ESL, sem XML.
  • Tiers: tier add/del/set state/set level/set position — também 100% dinâmico.

Ou seja, filas usam o mesmo padrão "arquivo + reload" dos Trunks; agentes e tiers (próxima fase) vão usar comandos ESL diretos, sem arquivo nenhum.

Mecanismo

Mesmo padrão dos gateways Sofia: um arquivo XML por fila (autoload_configs/callcenter_queues.conf.d/<queue_id>.xml, volume Docker compartilhado), incluído via X-PRE-PROCESS no nosso callcenter.conf.xml próprio (agente.md secao 15 — "carregar somente o necessário": zeramos os <agents>/<tiers> estáticos da vanilla, já que essas partes vão ser 100% dinâmicas).

Sequência de comandos confirmada manualmente contra o FreeSWITCH real (testei cada passo antes de escrever o código):

  1. queue load <nome> falha ("Invalid Queue not found!") se o arquivo foi adicionado depois do boot — a árvore XML em memória não sabe do arquivo novo ainda.
  2. reloadxml primeiro repopula essa árvore a partir do disco.
  3. Depois disso, queue reload <nome> sozinho já serve tanto pra criar quanto atualizar — não precisa distinguir load de reload.
  4. Fila removida: apagar o arquivo, reloadxml, queue unload <nome>.

b2bcall-fs-config (queue-sync.ts) reescreve todos os arquivos de fila habilitada (todos os tenants) a cada sync, remove os obsoletos, roda reloadxml uma vez, depois queue reload por fila desejada e queue unload por fila removida. Disparado por Redis pub/sub (b2bcall:queues:sync, mesmo mecanismo dos Trunks) a cada create/delete via API, e uma vez no boot do serviço.

Nome da fila no FreeSWITCH

<queue.id>@<tenant.telephonyDomain> — mesma convenção UUID dos gateways. Como o telephonyDomain hoje é compartilhado entre tenants (limitação já documentada em docs/EXTENSIONS.md), o unload de uma fila apagada usa o domain de "qualquer tenant ativo" como aproximação, já que o registro no banco já não existe mais nesse ponto pra sabermos o domain exato.

Verificado ponta a ponta

POST /queues {"name":"Suporte","strategy":"ROUND_ROBIN","maxWaitTime":120,"discardAbandonedAfter":90}
→ fs-config sincroniza (desired:1) → reloadxml + queue reload

callcenter_config queue list
→ <id>@b2bcall.local|round-robin|...|90|false|120|...  (parâmetros batem)

DELETE /queues/:id
→ fs-config sincroniza (removed:1) → reloadxml + queue unload
→ callcenter_config queue list volta vazia

O que falta

  • Agentes, Tiers, Pausas (secao 45-49) — implementados na fase seguinte, ver docs/AGENTS.md.
  • Monitoramento em tempo real das filas (secao 54) — depende de WebSocket multi-tenant, que ainda não existe.
  • tier-rule-wait-multiply-level e tier-rule-no-agent-no-wait (vistos no queue list da config vanilla) não são expostos como campos próprios ainda — ficaram de fora do escopo desta fase.
  • Quota de filas (max_queues) — depende de Plans/Entitlements.