From 61fdadc20260c7893d774be67d2a88ade781ec77 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 May 2026 22:49:00 +0000 Subject: [PATCH 1/4] Skip agent locking for bot agents in conditionalLockAgent Agent-Logs-Url: https://github.com/RocketChat/Rocket.Chat/sessions/81aad858-43f4-4eb1-88c8-72e52fbf9acd Co-authored-by: KevLehman <11577696+KevLehman@users.noreply.github.com> --- .../livechat/server/lib/conditionalLockAgent.ts | 12 ++++++++++++ .../server/lib/conditionalLockAgent.spec.ts | 16 ++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/apps/meteor/app/livechat/server/lib/conditionalLockAgent.ts b/apps/meteor/app/livechat/server/lib/conditionalLockAgent.ts index 44f4d416c18a1..667fccd0f1835 100644 --- a/apps/meteor/app/livechat/server/lib/conditionalLockAgent.ts +++ b/apps/meteor/app/livechat/server/lib/conditionalLockAgent.ts @@ -1,5 +1,6 @@ import { Users } from '@rocket.chat/models'; +import { hasRoleAsync } from '../../../authorization/server/functions/hasRole'; import { settings } from '../../../settings/server'; type LockResult = { @@ -22,6 +23,17 @@ export async function conditionalLockAgent(agentId: string): Promise }; } + const isBotAgent = await hasRoleAsync(agentId, 'bot'); + if (isBotAgent) { + return { + acquired: false, + required: false, + unlock: async () => { + // no-op + }, + }; + } + const lockTime = new Date(); const lockAcquired = await Users.acquireAgentLock(agentId, lockTime); diff --git a/apps/meteor/tests/unit/app/livechat/server/lib/conditionalLockAgent.spec.ts b/apps/meteor/tests/unit/app/livechat/server/lib/conditionalLockAgent.spec.ts index 50c7677db7bf4..582aafdf0cd5f 100644 --- a/apps/meteor/tests/unit/app/livechat/server/lib/conditionalLockAgent.spec.ts +++ b/apps/meteor/tests/unit/app/livechat/server/lib/conditionalLockAgent.spec.ts @@ -11,8 +11,11 @@ const mockSettings = { get: sinon.stub(), }; +const mockHasRoleAsync = sinon.stub(); + const { conditionalLockAgent } = proxyquire.noCallThru().load('../../../../../../app/livechat/server/lib/conditionalLockAgent', { '@rocket.chat/models': { Users: mockUsers }, + '../../../../../../app/authorization/server/functions/hasRole': { hasRoleAsync: mockHasRoleAsync }, '../../../settings/server': { settings: mockSettings }, }); @@ -21,11 +24,13 @@ describe('conditionalLockAgent', () => { mockUsers.acquireAgentLock.reset(); mockUsers.releaseAgentLock.reset(); mockSettings.get.reset(); + mockHasRoleAsync.reset(); }); describe('when waiting_queue is enabled', () => { beforeEach(() => { mockSettings.get.withArgs('Livechat_waiting_queue').returns(true); + mockHasRoleAsync.resolves(false); }); it('should return acquired: true when lock is successfully acquired', async () => { @@ -59,6 +64,16 @@ describe('conditionalLockAgent', () => { expect(mockUsers.releaseAgentLock.firstCall.args[0]).to.equal('agent1'); expect(mockUsers.releaseAgentLock.firstCall.args[1]).to.be.instanceOf(Date); }); + + it('should skip lock acquisition for bot agents', async () => { + mockHasRoleAsync.resolves(true); + + const result = await conditionalLockAgent('agent1'); + + expect(result.acquired).to.equal(false); + expect(result.required).to.equal(false); + expect(mockUsers.acquireAgentLock.called).to.equal(false); + }); }); describe('when waiting_queue is disabled', () => { @@ -72,6 +87,7 @@ describe('conditionalLockAgent', () => { expect(result.acquired).to.equal(false); expect(result.required).to.equal(false); expect(mockUsers.acquireAgentLock.called).to.equal(false); + expect(mockHasRoleAsync.called).to.equal(false); }); it('should have a no-op unlock function', async () => { From da6bce419c7c95882e3b210fef186d8bcdd0b1fd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 May 2026 23:25:03 +0000 Subject: [PATCH 2/4] Fix proxyquire stub key for hasRoleAsync in unit test Agent-Logs-Url: https://github.com/RocketChat/Rocket.Chat/sessions/309dd860-511d-4071-98ce-59ad731d784c Co-authored-by: KevLehman <11577696+KevLehman@users.noreply.github.com> --- .../unit/app/livechat/server/lib/conditionalLockAgent.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/tests/unit/app/livechat/server/lib/conditionalLockAgent.spec.ts b/apps/meteor/tests/unit/app/livechat/server/lib/conditionalLockAgent.spec.ts index 582aafdf0cd5f..4b79d29aa2bb9 100644 --- a/apps/meteor/tests/unit/app/livechat/server/lib/conditionalLockAgent.spec.ts +++ b/apps/meteor/tests/unit/app/livechat/server/lib/conditionalLockAgent.spec.ts @@ -15,7 +15,7 @@ const mockHasRoleAsync = sinon.stub(); const { conditionalLockAgent } = proxyquire.noCallThru().load('../../../../../../app/livechat/server/lib/conditionalLockAgent', { '@rocket.chat/models': { Users: mockUsers }, - '../../../../../../app/authorization/server/functions/hasRole': { hasRoleAsync: mockHasRoleAsync }, + '../../../authorization/server/functions/hasRole': { hasRoleAsync: mockHasRoleAsync }, '../../../settings/server': { settings: mockSettings }, }); From 4830115a7d66b1abf37ae3cc42b0dbaa904c0e14 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Fri, 22 May 2026 11:19:07 -0600 Subject: [PATCH 3/4] Create silent-coats-open.md --- .changeset/silent-coats-open.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/silent-coats-open.md diff --git a/.changeset/silent-coats-open.md b/.changeset/silent-coats-open.md new file mode 100644 index 0000000000000..e8e88a7966104 --- /dev/null +++ b/.changeset/silent-coats-open.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixes the Chat Limits locking mechanism to allow bot agents to skip the lock as they aren't limited From ff9649f3a2f554a3e1649858ffee687939a4264b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 22 May 2026 17:53:41 +0000 Subject: [PATCH 4/4] fix: combine shouldLock and isBotAgent checks into single condition --- .../livechat/server/lib/conditionalLockAgent.ts | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/conditionalLockAgent.ts b/apps/meteor/app/livechat/server/lib/conditionalLockAgent.ts index 667fccd0f1835..89935f3ec6470 100644 --- a/apps/meteor/app/livechat/server/lib/conditionalLockAgent.ts +++ b/apps/meteor/app/livechat/server/lib/conditionalLockAgent.ts @@ -10,21 +10,10 @@ type LockResult = { }; export async function conditionalLockAgent(agentId: string): Promise { - // Lock and chats limits enforcement are only required when waiting_queue is enabled + // Lock and chats limits enforcement are only required when waiting_queue is enabled and the agent is not a bot const shouldLock = settings.get('Livechat_waiting_queue'); - if (!shouldLock) { - return { - acquired: false, - required: false, - unlock: async () => { - // no-op - }, - }; - } - - const isBotAgent = await hasRoleAsync(agentId, 'bot'); - if (isBotAgent) { + if (!shouldLock || (await hasRoleAsync(agentId, 'bot'))) { return { acquired: false, required: false,