v3+
UCS Server - changelog
All notable changes to this project will be documented in this file.
[6.7.0] - 2026-11-13
🚀 Features
- (sso) SSO never takes over existing UCS users (ADR 0008)
- Users are matched only by external_id. An existing UCS user of the same
username without external_id logs in via SSO untouched and is skipped by
synchronization (no names, group, role, team, data or phones from AD).
Superusers and users with another external_id are refused. Pair users
explicitly with
ucs-msad external-ids --sql.
- Users are matched only by external_id. An existing UCS user of the same
username without external_id logs in via SSO untouched and is skipped by
synchronization (no names, group, role, team, data or phones from AD).
Superusers and users with another external_id are refused. Pair users
explicitly with
[6.6.0] - 2026-11-13
🚀 Features
- (sso) Ucs-msad external-ids pairs synchronized users with UCS users
- For migration of UCS with manually created users: lists users synchronization loads with external ID and status of matching UCS user (ok, set, conflict, superuser, new), matched by synchronized username, sAMAccountName part of username (--match account) or e-mail. --sql prints UPDATE setting external_id of paired users without one, conflicts and superusers are skipped. Changes column shows what synchronization changes on the user: username, active and group, role, team and template user of the matched AD group mapping.
🐛 Bug Fixes
- (sso) Principal lookup matches sAMAccountName of realm, ucs-msad sets LDAPSASL_CBINDING
- Principal lookup searched userPrincipalName only, so users whose UPN suffix differs from the realm (kloucek2@cdad.cz vs Kloucek@zap.cd.cz) were not found by MSAD.idp() login and ucs-msad user/attributes. Principal of the provider's realm is Kerberos principal sAMAccountName@REALM (ADR 0006) and now matches userPrincipalName or sAMAccountName, other realms userPrincipalName only.
ucs-msad imported python-ldap before ucs, so LDAPSASL_CBINDING=tls-endpoint set by ucs/init.py was not used and GSSAPI bind over ldaps:// to AD enforcing channel binding failed. ucs-msad sets it by default, environment value wins (none disables it).
[6.5.0] - 2026-11-13
🚀 Features
- (sso) Map AD attributes to extension, device, PSTN number and user data
- MSAD SSO settings "attributes" map AD attributes to targets extension, mac, device_type, filter (name or ID, fallback device_type_id / filter_id), pstn (users.number) and user_data key (merged into users.data), device display name template device_displayname.
Synchronization creates extension, softphone, primary extension and device
with line (when MAC is mapped) for users with extension by the same steps as
sync plugin, deletion is limited to msad:
ucs-msad attributes
See decisions/0007-ad-attribute-mapping.md (with login flow diagram). Requires ucs-data 0.103.
🐛 Bug Fixes
- (shared) Run change hook when shared data is edited in admin
- Change hooks (e.g. plugin storing cdkc_report as queue metrics) ran only for RPC provisioning.shared (Shared.set). Admin edit (config.shared.edit) stored the data without the hook, so plugins silently did nothing. Hook blocking the data with ProcessingError now rejects the admin edit too.
[6.4.0] - 2026-11-13
🚀 Features
- (sso) AD Principal usernames are Kerberos principal sAMAccountName@realm
- MSAD usernames = Principal now makes username sAMAccountName@realm (lowercase)
as in Kerberos ticket instead of userPrincipalName, so synchronized users match
uAuth Kerberos and AD password login with
username: principal. Users of installations with Principal usernames are renamed by next synchronization or login. ADR 0006.
- MSAD usernames = Principal now makes username sAMAccountName@realm (lowercase)
as in Kerberos ticket instead of userPrincipalName, so synchronized users match
uAuth Kerberos and AD password login with
[6.3.2] - 2026-11-13
🐛 Bug Fixes
- (sync) Identity management users from SSO have sync plugin keys
- config.sync.load returned SSO users with load_users() keys only, admin Identity management reads mac, device, number, filter and original of every user. SSO users now have the same keys as declared by the API (no phone, so mac/device/number/filter are None, original is empty).
[6.3.1] - 2026-11-13
🐛 Bug Fixes
- (sso) Send LDAP channel binding token over ldaps (AD LdapErr 80090346)
- AD enforcing LDAP channel binding rejects GSSAPI bind over ldaps:// without channel binding token as invalid credentials (data 80090346). python-ldap has no option for it, so LDAPSASL_CBINDING=tls-endpoint is set by default in ucs/init.py before python-ldap is imported (libldap reads it on import). LDAPS also looks for the RHEL/Fedora system CA bundle.
- (sync) Report unreachable SSO source in identity management
- Identity management showed no users when SSO source failed to load users (e.g. AD not reachable), now it reports error instead of empty list.
[6.3.0] - 2026-11-12
🚀 Features
- (sso) Add ucs-msad tool to check AD connection and query users and groups
- Reads MSAD SSO settings via UCS API and queries AD using ucs.sso.msad: list, check, groups, members, users, user (login result) and sync (dry run).
- (sso) Support ldaps:// DC URLs in Active Directory SSO
- DC may be hostname (ldap://) or URL ldap[s]://host[:port]. LDAPS verifies DC certificate against system CAs and sets SASL maxssf=0 (AD refuses GSSAPI sign/seal over TLS). krb5.conf gets hostnames only.
[6.2.0] - 2026-11-12
🚀 Features
- (metrics) Queue metrics and external queues
- Queue metric values are stored per queue and date period like user metrics, the queue is referenced by its brainless UUID. Patch 250 adds table queue_metrics. New RPC queues.metric.store/list/delete accepts external queues and UCS inbound queues.
External queues (queues handled by external systems, e.g. OSL) are the first brainless-native config object: no PostgreSQL table, RPC config.external_queues.list/get/add/edit/delete writes the brainless collection directly. Requires ucs-data 0.102.
Metric storage moved to package ucs/metrics: ucs.metrics.agents and ucs.metrics.queues (ucs.agents.metrics removed). ADR 0005 now covers all metrics and external queues.
[6.1.0] - 2026-11-12
🚀 Features
- (recording) Allow users to pause and resume call recording
- Agents need to stop recording while a customer dictates sensitive data, for constant recording (trunk incoming route, outgoing filter) as well as recording on demand.
New RPC methods call.record.pause and call.record.resume mute or unmute MixMonitor via AMI MixMonitorMute on every channel of the call carrying the recording. Silence is written while paused, so the recording keeps the call length and stays a single file; conversion and storage are unchanged.
Patch 248 adds role columns recording_pause (permission) and recording_pause_timeout (auto resume after N seconds, empty = never). Auto resume timers are per channel, so they survive call changes on transfer. A newly started MixMonitor is never paused.
Calls and channels expose recording_paused, call updates notify clients.
- (metrics) Store custom user metrics from external systems and plugins
- Admins define metrics (key, unit, rollup, group) and values are stored per user and date period. External systems write them over RPC with the uAuth JWT header, UCS plugins call ucs.agents.metrics.store().
Patch 249 adds tables metrics and user_metrics. Periods of one user and metric never overlap (gist EXCLUDE, btree_gist): writing the same period again overwrites the value, a different overlapping period is rejected.
New RPC methods config.metrics.list/get/add/edit/delete manage definitions, agent.metric.store/list/delete manage values by metric key with optional rollup (sum, avg, min, max, last). See ADR 0005.
🐛 Bug Fixes
- (email) Check email source group ACL for source filters
- Filters have no group_id, so add always checked ACL on group 1 and edit/delete failed with KeyError for non-super users. Check the ACL on the group of the filter's email source instead, and on the target source too when a filter is moved.
Dialplan
- Record internal calls before entering dispatch workspace
[6.0.0] - 2026-11-09
🚀 Features
-
[breaking] UAuth provisioning and refresh of user sessions
-
- auth.lookup has no side effects (unknown user is not created by SSO) and returns id and superuser of the user
-
-
auth.provision creates or updates user from profile read by uAuth from MS AD, using the same mapping as user synchronization (Sync.sync_sso_user)
-
uAuth refreshes sessions of users changed, deleted or renamed in UCS and of users of changed role (rpc.uauth.refresh)
-
Fill user metadata into templates as variables
-
Use aiobp NATS connector and add nats.* API for key-value buckets
- NATS connector is now a thin layer over aiobp.nats keeping the UCS wire format (DecimalEncoder JSON) and the synchronous rpc/bucket access.
-
UCS API is served as rpc.ucs.
to every UCS instance, invalid input errors reply their field errors as before -
every RPC reply code other than 1 raises ProcessingError, error replies with negative codes (e.g. from uAuth) were returned as results before
-
consumer loops stop on shutdown (they ran forever before)
-
NATS events are handled in arrival order across all subjects
New superuser-only API for NATS key-value buckets: nats.buckets, nats.bucket.keys (paging, ordering, values picked from JSON), and nats.bucket.get/set/del with revision checks. Changes emit nats.bucket.update event with old and new value for audit.
Also fixes Json validator crashing on invalid input.
-
OpenVPN configured by ucs-root
- UCS issues the OpenVPN key and certificate and sends the configuration to ucs-root's openvpn.apply without waiting for the result, it runs in the callbacks thread instead of its own one. ucs-root restarts OpenVPN only when something changed, failures are in its journal. HOST changes reprovision OpenVPN too, it's pushed to clients as a route.
-
polkit rule and ConditionFileNotEmpty drop-in removed, also from installed systems by after-install.sh, files given back to root
-
ucs is no longer in openvpn group, polkit dependency removed
-
ucs/root.py: send() copied from ucs-root example client
VPN_SERVICE and UCS_VPN_*_FILENAME parameters are no longer used.
Requires ucs-root with openvpn.apply.
- (teamchat) Colleague chat with groups, receipts and attachments, replaces presence session chat
- (teamchat) Events on conversation subjects routed by ucs-users header
- UCS.notify() and Nats.notify() accept NATS headers, routing_headers() builds ucs-users/ucs-groups. Team chat publishes on teamchat.{conversation_id} with recipients in ucs-users: one publish per message and receipt instead of one per member. client_msg_id is no longer stripped for other members.
- (voicemail) Renumber folders and keep at most 9000 messages
- Asterisk numbers a new message as the highest existing number + 1 and never reuses gaps, so folders managed through UCS ran into its 9999 limit and new messages overwrote msg9999. UCS now removes the oldest messages above 9000 per folder and renumbers the rest from msg0000, keeping voicemail_messages.filename in sync, under Asterisk's folder lock. Runs at startup, daily, when a message gets number 9000 or more, and on demand via voicemail.renumber (superusers).
See decisions/0004-voicemail-renumbering.md.
🐛 Bug Fixes
- (brainless) Decode user data stored as varchar JSON
- (recording) Add uniqueid to recording.new event
- The recording.new payload is the recording row, which carries the CDR uniqueid as object_id. Event notifier rules using %uniqueid% got an empty value for it. uniqueid is now added as an alias of object_id.
📚 Documentation
- Add design decisions (ADRs)
- Architecture decision records for decisions spanning UCS components, moved here from the workspace root: NATS routing headers, team chat conversation subjects and snake_case naming for new Python code.
⚙️ Miscellaneous Tasks
- Remove keys that we synchronize into Brainess that aren't there
- Make brainless.db public
- Ucs-data 0.101 (NATS routing headers, team chat conversation subjects)
[5.19.0] - 2026-11-05
🚀 Features
- Added email feature to mobile_features in roles
- (email) Subject of new e-mail from whole body template
- A whole body template can carry a subject with the same variables as the body. It is set on new e-mails only, replies and forwards keep their subject. %ORIGINAL% in the subject is always empty.
- Link e-mail replies to tasks in queues without task queue
- Thread matching in EmailTasks.delivered() was skipped entirely when the e-mail queue had no new_task_queue_id, so replies to e-mails linked to manually created tasks were never paired. Match the thread for every e-mail queue and only gate creating new tasks (and closing unmatched automatic replies) on new_task_queue_id.
🐛 Bug Fixes
- (recording) Transcription_status 0 means never transcribed, 10 waiting for AI driver
🚜 Refactor
- (brainless) Store data with ingest() and log invalid values
- Use BrainlessDB 1.0 ingest() instead of setattr()+save(), so one invalid value no longer fails the whole update. Values that could not be stored as given are logged as errors. Fixed security registry attribute names ca_crl_conf and ca_crt_conf.
⚙️ Miscellaneous Tasks
- Bump Brainless version to 1.1.0
[5.18.1] - 2026-11-01
🐛 Bug Fixes
- (sms) O2 gateway fails certificate verification on Python 3.13+
- Python 3.13 enabled VERIFY_X509_STRICT in ssl.create_default_context(), which rejects the O2 CA certificate because its Basic Constraints extension is not marked critical. Disable the strict flag for the O2 context, server certificate is still verified against the O2 CA.
[5.18.0] - 2026-11-01
🚀 Features
-
Synchronize users from Active Directory SSO provider
- Simpler alternative to the sync plugin built into core. When no sync plugin is installed, sync.synchronize() synchronizes users of every MSAD SSO provider with the new "sync" option enabled:
-
MSAD group mapping extended with group_id, role_id and team_id (user_id template kept for SSO login); mapping is applied also to users created on first SSO login
-
members of mapped groups are loaded via LDAP (paged search, nested membership via LDAP_MATCHING_RULE_IN_CHAIN), first matching group wins
-
users are created/updated (name, e-mail, active, group, role, team), existing users without external ID are paired by username, users no longer member of any mapped group are deactivated (never deleted)
-
external ID is msad:
: -
no groups, extensions or devices are created
-
MSAD login as identity provider with nested group mapping
- SSO login no longer verifies passwords through GSSAPI, MSAD provides identity of already authenticated user from AD (idp()):
-
user is mapped by the first matching AD group mapping, membership counts directly, through primary group and nested (same rule as sync)
-
group mapping sets settings_user_id (inherit provisioning settings) instead of cloning template user_id, "domains" setting is removed
-
fix SID decoding on Python 3 which broke every idp() lookup, primary group is resolved by its SID instead of listing all groups in domain
-
usernames are escaped in LDAP filters
-
krb5.conf domain_realm is written once per realm, not once per DC
-
sync uses every SSO handler with load_users() and sync enabled, frontend or backend
-
gssapi Python dependency and libkrb5-dev dropped
-
tests/msad_check.py tests MSAD against real AD with Kerberos ticket
-
Link e-mails to tasks, return tasks to last agent, automatic task assignment
-
- Link received e-mails to tasks by Message-ID/References threading, reopen recently closed tasks on customer reply, tag and close automatic replies
-
-
Link outgoing replies to task of original e-mail, optionally close task on reply or when e-mail is closed unreplied (forwards are never treated as replies)
-
Return task moved back into queue to its last agent in that queue
-
Automatically assign tasks to agents up to team minimum, capped by maximum
-
E-mail edit API task_id checks task access and records link in both histories
-
E-mail sender option save_original stores sent message as original.msg
-
Background DB patches (patchNNN.background.sql/py) tracked in patches table, run after UCS start with own connection; patch 244 backfills in batches
🐛 Bug Fixes
- Always include original e-mail when forwarding
- A forward lost the original e-mail when the queue default template had no %ORIGINAL% placeholder, or when the queue had no default template at all. Add the original after the template content in the first case and use it as the whole body in the second. Replies are unchanged.
- Keep forward when applying template to a forward draft
- template.use took the forward flag only from the client, which does not send it when an agent applies a body template to an existing draft. The original e-mail was then quoted as a reply instead of forwarded. Use the forward flag stored on the draft as well.
- Apply template with the e-mail the draft was created from
- A reply or forward draft found its original e-mail through reply_id, which is set on the first e-mail of the thread. When the agent replied to or forwarded an outbound e-mail, the draft was created from that e-mail, but applying a template later quoted or forwarded the thread's first e-mail instead.
Store the e-mail the agent replied to or forwarded in a new emails.original_id column (DB patch 243) and use it when applying a template. Drafts created before the patch fall back to reply_id.
- Keep original attachments when applying template to a draft
- Applying a body template removes all of the draft's attachments, which on a forward includes the ones copied from the original e-mail. Now that the template puts the original body back in, the forward lost its attachments and its inline images pointed to the original e-mail.
Move copying of the original's attachments from the reply API into Email.copyOriginalAttachments() and call it from useTemplate() whenever the original ends up in the new body: all attachments for a forward, inline images for a reply.
- Copy default template attachments to new e-mails
- A new e-mail took only the HTML of the queue's default template. The template's attachments were never copied and the picture links kept pointing at the template, so pictures were missing in the sent e-mail. Applying the same template manually worked because useTemplate() copies the attachments.
Move the copying into _addTemplateAttachments() and use it in add() as well. reply() applies the template itself afterwards, so it tells add() to skip it.
Picture links are now always pointed at the e-mail's own copy, not only when the attachment IDs differ, and the link to template attachment 1 no longer matches attachment 10.
- Unpair lead user when called don't answer the call so anyone else can try it later
📚 Documentation
- Explain reply_to vs. original_id on reply and forward drafts
🧪 Testing
- Cover Content-Type name parameter on attachments
- Add uuid and inbound to e-mail test fixtures
- sender.py reads email['uuid'] for Message-ID since 3b39cf1c and api/email.py reply() reads email['inbound'], but the fixtures didn't have them, so 9 tests failed with KeyError.
⚙️ Miscellaneous Tasks
- Ignore Claude Code worktrees and local settings
- Log thread utilization to /var/spool/ucs, added cluster create users, removed old docs
[5.17.1] - 2026-10-25
🐛 Bug Fixes
- Give asterisk group access to MOH classes /var/lib/asterisk/moh-
[5.17.0] - 2026-10-25
🚀 Features
-
Run UCS as unprivileged ucs user, system configuration by ucs-root
- UCS runs as ucs user (NoNewPrivileges, UMask 0002) and asks ucs-root over /run/ucs/root.socket to apply network, firewall, hostname, NTP, SMTP, DHCP and web certificate configuration (ucs/root.py, copy of ucs-root's example client). provisioning/system.py collects the data from the database as before and sends it as structured parameters.
-
package: ucs user in uauth, asterisk and openvpn groups, owns its log, spool and TFTP directories, depends on ucs-root
-
after-install.sh: ownership for ucs, /etc/ucs/ucs.conf ucs:ucs 0600, web key root:traefik 0640, Asterisk directories group writable, /etc/krb5.conf and OpenVPN files group writable by ucs, DHCP files back to root, old O2 SMS keys removed from /etc/ssl
-
/var/spool/ucs owner is changed in background by ucs-spool-chown (systemd-run, newest e-mails first), it may take hours
-
OpenVPN: polkit lets ucs start/stop/restart openvpn-server@ucs, enabled by the package, a drop-in skips it while its configuration is empty
-
O2 SMS gateway loads certificates from memory (memfd), never from disk
-
keytab made readable by uauth group before start
[5.16.2] - 2026-10-23
🐛 Bug Fixes
- Add name parameter to attachment Content-Type for older mail clients
- Some clients (old Outlook, Lotus Notes, ...) read the attachment name only from the deprecated Content-Type name parameter and show nameless/generic attachments otherwise. Non-ASCII names are sent there as RFC 2047 encoded-words, since those clients don't understand RFC 2231.
- Split postgres initialization and create/upgrade to after install/transaction
[5.16.1] - 2026-10-22
⚙️ Miscellaneous Tasks
- Use .python-version as source of truth
[5.16.0] - 2026-10-21
🚀 Features
- UAuth credentials, lookup and impersonation API via NATS
-
- auth.credentials verifies username and password without creating session
-
- auth.lookup checks user authenticated by uAuth is active and returns apps
- auth.impersonate tells uAuth whether its user may impersonate another one
- password verification of sessions.login split to reusable verify()
- password of auth.credentials is masked in request log
- install uAuth login.d/50_ucs.yaml and lookup.yaml
[5.15.3] - 2026-10-21
🐛 Bug Fixes
- Create /var/lib/tftp on Ubuntu too
[5.15.2] - 2026-10-21
🐛 Bug Fixes
- Create /run/postgresql before restarting PostgreSQL on RHEL
- The restart failed with "could not create lock file /var/run/postgresql/.s.PGSQL.5432.lock". The directory comes from tmpfiles.d, which systemd's rpm file trigger applies only after all %posttrans scriptlets, so after-transaction.sh now creates it with systemd-tmpfiles first.
[5.15.1] - 2026-10-21
🐛 Bug Fixes
- Restart PostgreSQL and create database after the dnf transaction
- On RHEL "systemctl restart postgresql" failed inside the dnf transaction. The restart and create.sh move to the packager's after_transaction hook (rpm %posttrans, end of postinst on deb), which also starts ucs.service again now that the database exists. Hook messages use pkg_log/pkg_warn.
Needs the packager with after_transaction and pkg_log (insoft/deploy).
[5.15.0] - 2026-10-20
🚀 Features
- Added username to structure.enums user list
🐛 Bug Fixes
- Dialer lost lead clid_number when outbound has no numbers
⚙️ Miscellaneous Tasks
- Moved to INSOFT Python 3.14
[5.14.8] - 2026-10-19
🐛 Bug Fixes
- Libmysql client has different versions in Ubuntu noble and resolute
[5.14.7] - 2026-10-19
🐛 Bug Fixes
- Typo in package.yaml tftp path
[5.14.6] - 2026-10-19
🐛 Bug Fixes
- Upgrade to aiobp 3.0.1
[5.14.5] - 2026-10-19
🐛 Bug Fixes
- Hold aiobp < 3.0.0
[5.14.4] - 2026-10-17
🐛 Bug Fixes
- (dispatch) Only force call.connect for a leg ucs-ami already reported up
⚙️ Miscellaneous Tasks
- Switched to new build system
[5.14.3] - 2026-10-14
🐛 Bug Fixes
- Handle Decimal encoded timestamp in Brainless
- Fallback to context if trunk peer name differs
[5.14.2] - 2026-10-13
🐛 Bug Fixes
- Use configured log level for console and file
[5.14.1] - 2026-10-12
🐛 Bug Fixes
- Store called number instead of "ringing" for short calls
- Avoid issuing multiple call.connected event for dialer calls
- (queue) Park ucs-ari agent events until ucs-ami announces the channel
[5.14.0] - 2026-10-11
🚀 Features
- (email) Add Message-ID header to outgoing e-mails
- Outgoing e-mails currently carry no Message-ID, so a downstream bounce/DSN can't be reliably matched back to the originating e-mail record. Embed our e-mail DB id in a generated Message-ID (<email-id-{id}@{hostname}>, preferring the public hostname when configured) so future bounce-handling can look up the right record. This is groundwork only; bounce/DSN parsing is not implemented yet.
- Added optional team ID limit to queues.agentsLogout and added queues.agentsLogin
🐛 Bug Fixes
- (email) Encode non-ASCII attachment filenames per RFC 2231
- Content-Disposition was built manually as an f-string with the filename wrapped in email.header.Header. Since that whole string was passed as a single opaque header value, Python's generator RFC 2047 encoded the entire "attachment; filename=..." value as one blob when the filename contained non-ASCII characters (e.g. Czech diacritics). RFC 2047 encoded-words are invalid inside structured headers like Content-Disposition, so mail clients couldn't parse out the attachment disposition or filename at all, leaving the attachment unnamed.
Use add_header's filename= keyword instead, which lets the email module apply correct RFC 2231 parameter encoding (filename*=utf-8''...) to just the filename, leaving the rest of the header intact and parseable.
- Use UUID in email Message-ID
- (email) Add Cc header to outgoing queued e-mails
- CC recipients were delivered (they are part of the SMTP envelope) but the message had no Cc header, so they looked like BCC to everyone and were dropped on reply-all. BCC recipients stay envelope-only.
- (email) Add Cc header to outgoing queued e-mails
- CC recipients were delivered (they are part of the SMTP envelope) but the message had no Cc header, so they looked like BCC to everyone and were dropped on reply-all. BCC recipients stay envelope-only.
- (status) Don't re-hang up an already finalized call on late setCall (duplicate CDR/RTCP)
- (mobile) Skip a call push the device has no token for instead of a KeyError
- (call) Raise dial control timeout above the dispatch ringing delay
- (call) Keep the 10s control timeout for dial only, answer/hangup back to 4s
- (call) Finish call.hangup on the line's cancel, a dispatch call never names it in the final hangup
[5.13.0] - 2026-09-24
🚀 Features
- Added SIPREC shadow calls
[5.12.3] - 2026-09-24
🐛 Bug Fixes
- (queue) Pair auto-answered agent legs without a phantom dialplan call
[5.12.2] - 2026-09-22
🐛 Bug Fixes
- Return leads in selected outbound campaign only in API
[5.12.1] - 2026-09-20
🐛 Bug Fixes
- Issue event on agent stats reset to synchronize Brainless
[5.12.0] - 2026-09-20
🚀 Features
- Allow tag in outgoing email sanitizer
🐛 Bug Fixes
- Synchronize agent first and last login/ready and logout times to BrainlessDB
[5.11.2] - 2026-09-18
🐛 Bug Fixes
- (brainless) Stamp last_interaction at first login of the day so leastrecent ignores status flips
[5.11.1] - 2026-09-18
🐛 Bug Fixes
- (brainless) Stamp last_interaction at login so leastrecent ignores AUX/ACW flips
[5.11.0] - 2026-09-16
🚀 Features
- Added SIP trunk DID source support for To and P-Called-Party-ID headers
🐛 Bug Fixes
- Task queue removal
⚙️ Miscellaneous Tasks
- Upgraded aiobp to v2.1
[5.10.3] - 2026-09-13
🐛 Bug Fixes
- Allow call transfers to not READY agents too
[5.10.2] - 2026-09-11
🐛 Bug Fixes
- Don't indicate ringing for dialplan applications
- Search in multiple number leads
- (yealink) Hang up SIP-T21P E2 via Asterisk, its CANCEL key toggles mute
- Honor dispatch workspace abandoned outbound queue for direct phone calls too
[5.10.1] - 2026-09-11
🐛 Bug Fixes
- Forward offline for logged-in user in dispatch workspace
⚙️ Miscellaneous Tasks
- Remove stale uPhone softphones on startup
[5.10.0] - 2026-09-10
🚀 Features
- Added uCRM, tag and template view permissions to ACL
[5.9.0] - 2026-09-07
🚀 Features
- (dispatch) Configurable ringing of calls with no agent assigned
- Added working_time indicator to outbound queue status
🐛 Bug Fixes
- (booking) _set_agent_peer no-op guard
- (dispatch) Drop RingMode import that broke UCS start-up
- (agents) Stamp last_interaction when a call leaves the agent, not at final hangup
- (email) Reassing incoming e-mail when replying on reply
[5.8.1] - 2026-09-01
🐛 Bug Fixes
- Removed accidentaly added reporting plugin
[5.8.0] - 2026-09-01
🚀 Features
- Added full-text search to e-mails
🐛 Bug Fixes
- Resolve current_peer on mobility login, reconcile stale values, only assign agent's own line
[5.7.5] - 2026-08-25
🐛 Bug Fixes
- Use dialed legs count instead of call state for CANCEL logic
- Issue EVENT_CALL_CANCEL when agent doesn't answer call from queue
- Force EVENT_CALL_CANCEL on external agent phones in PSTN
- Typo in Channel dispatchPendingEvents -> dispatchPendingQueueEvents
[5.7.4] - 2026-08-25
🐛 Bug Fixes
- Don't release agents with external calls into booking with reconcile
[5.7.3] - 2026-08-24
🐛 Bug Fixes
- Handle multiple call blind transfers via queues
⚙️ Miscellaneous Tasks
- Moved PostgreSQL indexing logic to sql module
[5.7.2] - 2026-08-23
🐛 Bug Fixes
- (nats) Catch OSError, not the undefined OSerror, and do not block the loop while retrying
⚙️ Miscellaneous Tasks
- Changed INSOFT kiosk deploy directory (for BeFree)
[5.7.1] - 2026-08-23
🐛 Bug Fixes
- (call) Refuse a transfer to a busy agent instead of dropping the customer
- (nats) Stop doubling every UCS API call after a NATS reconnect
[5.7.0] - 2026-08-22
🚀 Features
- (softphone) Added device into user mobility events
🐛 Bug Fixes
- Update brainlessdb
[5.6.6] - 2026-08-20
🐛 Bug Fixes
- (bridge) Don't register the semi-attended transfer transport as a dialed destination
- (call) Fall back to the default outgoing filter when the extension has none
- (bridge) Drop the destroyed-channel guard superseded by the ari single-publish fix
[5.6.5] - 2026-08-19
🐛 Bug Fixes
- (call) Survive semiattended transfers whose destination is not the Dial app
[5.6.4] - 2026-08-18
🐛 Bug Fixes
- (provisioning) Update version of settings when deleting template user
- (call) Set proper user_id on call when dialing user's external number
[5.6.3] - 2026-08-15
🐛 Bug Fixes
- Upgrade braunlessdb
[5.6.2] - 2026-08-15
🐛 Bug Fixes
- (brainless) Restore agent answered-stats update in UCS (revert 1baaddd6)
[5.6.1] - 2026-08-15
🐛 Bug Fixes
- (call) Exclude hung up calls immediately from API call.current
- (email) Don't reassign original e-mail agent on reply
- (brainless) Move agent answered calls stats update to ARI
- (emal) Change queue and agent in reply when changed original message
[5.6.0] - 2026-08-12
🚀 Features
- (dispatch) Added abandoned calls to outbound campaign feature
⚙️ Miscellaneous Tasks
- (email) Unify e-mail reply and forward template usage
[5.5.0] - 2026-08-11
🚀 Features
- (email) Allow additional replies to e-mails
🐛 Bug Fixes
- (brainless) Resync device lines into the bucket on device.line.* events instead of only at startup
[5.4.1] - 2026-08-06
🐛 Bug Fixes
- (call) Set last dialed channel on broadcast queue connect to have connected as B side instead of last rang channel
[5.4.0] - 2026-08-06
🚀 Features
- (email) Added configurable number of automatically assigned emails
- (phone) Added configurable offline forward if no agent is associated
🐛 Bug Fixes
- (email) Fix sanitizer configuration so href is not stripped from anchor...
- (call) Don't hangup call on blind transfer, cancel it and reset connected information
🚜 Refactor
- (ai) Keep id/uniqueid out of stored transcription
[5.3.11] - 2026-08-04
🐛 Bug Fixes
- (stats) Ignore non-agent changes in agent first login time
- (call) Generate event for semiattended call transfers from ARI queues
[5.3.10] - 2026-07-31
🐛 Bug Fixes
- (status) Emit terminal call.hangup for blind-transferer and detach them from a call continuing to an external destination
[5.3.9] - 2026-07-30
🐛 Bug Fixes
- (callcontrol) Link blind-transfer-into-queue Local caller to its bridge peer so queue events pair and agents see the call
[5.3.8] - 2026-07-28
🐛 Bug Fixes
- (nats) Handle race condition when UCS AMI starts after UCS server
[5.3.7] - 2026-07-24
🐛 Bug Fixes
- (email) Fix sanitizer configuration so href is not stripped from anchor elements in outgoing emails
- (email) Allow links in outgoing emails
- (email) Prefer html multipart alternative
- (booking) Release immediate reservations and periodically reconcile agent peer state so a missed event can't starve a READY agent
🚜 Refactor
- (email) Avoid reusing queue variable
- (email) Rename reused variables
- (email) Use where_ prefix for assignEmail filter variables
- (email) Rename reused variables
- (crm) Remove dead None initializations
- (email) Rename reused variables
- (email) Remove Any from agent_status typing
- (email) Remove Any from updateNumbers
🎨 Styling
- Apply ruff formatting
- (email) Apply ruff formatting
⚙️ Miscellaneous Tasks
- (ruff) Ignore B904, we don't use raise from
[5.3.6] - 2026-07-17
🐛 Bug Fixes
- (brainless) Map SQL penalty to KV priority in user.queue updates so supervisor priority changes reach ari
- (config) Include suspended in queue agents select so queue settings saves don't wipe suspension from the KV
[5.3.5] - 2026-07-16
🐛 Bug Fixes
- (brainless) Sync runtime agent suspend/resume into the inbound KV so ari stops ringing suspended agents
[5.3.4] - 2026-07-16
🐛 Bug Fixes
- (brainless) Stamp last_interaction at call hangup instead of connect so leastrecent orders by reliable, final, end-of-call data
[5.3.3] - 2026-07-15
🐛 Bug Fixes
- (status) Surface ringing of Stasis-blind-transferred calls to the target operator (link orphan Local pair via bridge, guard same-call setCall, ignore hold/unhold from transferred-away channels)
- (status) Repair Local pair mislinked to its own dial target instead of creating a degenerate self-call
[5.3.2] - 2026-07-14
🐛 Bug Fixes
- (provisioning) Import ORIG_CALLER/LAST_EXTENSION from transferer on blind transfer of Stasis calls
- Add missing newline
🚜 Refactor
- (queue) Add type hints to queue.py
🧪 Testing
- Enable pytest unit tests for ucs modules
- Add tests for email
⚙️ Miscellaneous Tasks
- Replace ruff select ALL with a curated rule set
- (agent) Add type hints to EmailQueueAgent
- (email) Add type hints to Email state attributes
- (email) Annotate local collections in Email
- (email) Annotate normalize_attachments return and stats_stored
- (email) Annotate load() where dict
- (mypy) Add override for untyped ucs_data
- (ruff) Enable W rules to catch missing EOF newline
[5.3.1] - 2026-07-14
🐛 Bug Fixes
- (agents) Call agents booking in run method so we don't override thread start method
[5.3.0] - 2026-07-13
🚀 Features
- (phone) Make complete provisioning for INSOFT kiosk
🐛 Bug Fixes
- (email) Unpack multiple values in email attachment sender
- Added external number handling into agent booking
- Booking start order fix
- Trigger call.connected when dispatch channel joins
- (dispatch) Emit call.connected when agent joins dispatch bridge
- (email) Read attachment fields by key instead of unpacking dict
- (transfer) Normalize external targets for blind/consult transfer so they route via filter-internal
⚙️ Miscellaneous Tasks
- Add email sender typing to enhance type safety
- (email) Correct attachment types in outgoing message
- Add dev dependency group
- Move mypy config into pyproject.toml
- Fix deprecated ruff lint config
[5.2.0] - 2026-06-25
🚀 Features
- (queues) Added agent reservation for multichannel
- (queues) Return peer and line_id in successful agent reservation
🐛 Bug Fixes
- (sync) Fixed logging of IDP synchronization
- (uphone) Activate and deactivate uPhone for INSOFT kiosk device ID too
- (mobility) Always issue all currently associated lines in UM login event
⚙️ Miscellaneous Tasks
- (queues) Accept agent ID as argument in reservation
[5.1.0] - 2026-06-16
🚀 Features
- (phone) Added INSOFT kiosk device provisioning
🐛 Bug Fixes
- (nats) Subscribe to all events in main thread
- (inbound) Handle inbound queue agent external phones in PSTN
- (email) Discard empty blockquotes from messages
[5.0.5] - 2026-06-15
🐛 Bug Fixes
- (ai) Make workflow_logical_id optional for OSL voicebot
⚙️ Miscellaneous Tasks
- (calls) Be verbose about inbound queue events