feat: Add temporary account setting to disable Captain auto-resolve (#13680)

Add a temporary `captain_disable_auto_resolve` boolean setting on
accounts to prevent Captain from resolving conversations. Guards both
the scheduled resolution job and the assistant's resolve tool.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Aakash Bakhle
2026-02-27 23:07:00 +05:30
committed by GitHub
parent 14b4c83dc6
commit c08fa631a9
7 changed files with 44 additions and 0 deletions

View File

@@ -41,6 +41,7 @@ class Account < ApplicationRecord
'audio_transcriptions': { 'type': %w[boolean null] },
'auto_resolve_label': { 'type': %w[string null] },
'keep_pending_on_bot_failure': { 'type': %w[boolean null] },
'captain_disable_auto_resolve': { 'type': %w[boolean null] },
'conversation_required_attributes': {
'type': %w[array null],
'items': { 'type': 'string' }
@@ -90,6 +91,7 @@ class Account < ApplicationRecord
store_accessor :settings, :audio_transcriptions, :auto_resolve_label
store_accessor :settings, :captain_models, :captain_features
store_accessor :settings, :keep_pending_on_bot_failure
store_accessor :settings, :captain_disable_auto_resolve
has_many :account_users, dependent: :destroy_async
has_many :agent_bot_inboxes, dependent: :destroy_async

View File

@@ -2,6 +2,8 @@ class Captain::InboxPendingConversationsResolutionJob < ApplicationJob
queue_as :low
def perform(inbox)
return if inbox.account.captain_disable_auto_resolve
Current.executed_by = inbox.captain_assistant
resolvable_conversations = inbox.conversations.pending.where('last_activity_at < ? ', Time.now.utc - 1.hour).limit(Limits::BULK_ACTIONS_LIMIT)

View File

@@ -12,6 +12,7 @@ module Enterprise::Account::ConversationsResolutionSchedulerJob
inbox = captain_inbox.inbox
next if inbox.email?
next if inbox.account.captain_disable_auto_resolve
Captain::InboxPendingConversationsResolutionJob.perform_later(
inbox

View File

@@ -6,6 +6,7 @@ class Captain::Tools::ResolveConversationTool < Captain::Tools::BasePublicTool
conversation = find_conversation(tool_context.state)
return 'Conversation not found' unless conversation
return "Conversation ##{conversation.display_id} is already resolved" if conversation.resolved?
return 'Auto-resolve is disabled for this account' if conversation.account.captain_disable_auto_resolve
log_tool_usage('resolve_conversation', { conversation_id: conversation.id, reason: reason })

View File

@@ -64,4 +64,15 @@ RSpec.describe Captain::InboxPendingConversationsResolutionJob, type: :job do
}
)
end
it 'does not resolve conversations when auto-resolve is disabled at execution time' do
inbox.account.update!(captain_disable_auto_resolve: true)
expect do
described_class.perform_now(inbox)
end.not_to(change { resolvable_pending_conversation.reload.status })
expect(resolvable_pending_conversation.reload.status).to eq('pending')
expect(resolvable_pending_conversation.messages.outgoing).to be_empty
end
end

View File

@@ -30,6 +30,22 @@ RSpec.describe Account::ConversationsResolutionSchedulerJob, type: :job do
end
end
context 'when account has captain_disable_auto_resolve enabled' do
let!(:regular_inbox) { create(:inbox, account: account) }
before do
create(:captain_inbox, captain_assistant: assistant, inbox: regular_inbox)
account.update!(captain_disable_auto_resolve: true)
end
it 'does not enqueue resolution jobs' do
expect do
described_class.perform_now
end.not_to have_enqueued_job(Captain::InboxPendingConversationsResolutionJob)
.with(regular_inbox)
end
end
context 'when inbox has no captain enabled' do
let!(:inbox_without_captain) { create(:inbox, account: create(:account)) }

View File

@@ -36,6 +36,17 @@ RSpec.describe Captain::Tools::ResolveConversationTool do
end
end
describe 'when auto-resolve is disabled for the account' do
before { account.update!(captain_disable_auto_resolve: true) }
it 'does not resolve and returns a disabled message' do
result = tool.perform(tool_context, reason: 'Possible spam')
expect(result).to eq('Auto-resolve is disabled for this account')
expect(conversation.reload).not_to be_resolved
end
end
describe 'resolving an already resolved conversation' do
let(:conversation) { create(:conversation, account: account, inbox: inbox, status: :resolved) }