From 2e5cb20856e399735714a42f54125911969acf81 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 23 Oct 2025 12:19:33 +0530 Subject: [PATCH] feat: ensure activity message for handoff --- enterprise/lib/captain/tools/handoff_tool.rb | 4 ++ .../lib/captain/tools/handoff_tool_spec.rb | 51 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/enterprise/lib/captain/tools/handoff_tool.rb b/enterprise/lib/captain/tools/handoff_tool.rb index 49f7c5a65..4301a53f9 100644 --- a/enterprise/lib/captain/tools/handoff_tool.rb +++ b/enterprise/lib/captain/tools/handoff_tool.rb @@ -24,6 +24,8 @@ class Captain::Tools::HandoffTool < Captain::Tools::BasePublicTool private def trigger_handoff(conversation, reason) + Current.executed_by = @assistant + # post the reason as a private note conversation.messages.create!( message_type: :outgoing, @@ -36,6 +38,8 @@ class Captain::Tools::HandoffTool < Captain::Tools::BasePublicTool # Trigger the bot handoff (sets status to open + dispatches events) conversation.bot_handoff! + ensure + Current.executed_by = nil end # TODO: Future enhancement - Add team assignment capability diff --git a/spec/enterprise/lib/captain/tools/handoff_tool_spec.rb b/spec/enterprise/lib/captain/tools/handoff_tool_spec.rb index 16b46c08a..8b52ed3d6 100644 --- a/spec/enterprise/lib/captain/tools/handoff_tool_spec.rb +++ b/spec/enterprise/lib/captain/tools/handoff_tool_spec.rb @@ -29,6 +29,8 @@ RSpec.describe Captain::Tools::HandoffTool, type: :model do describe '#perform' do context 'when conversation exists' do context 'with reason provided' do + before { conversation.update!(status: :pending) } + it 'creates a private note with reason and hands off conversation' do reason = 'Customer needs specialized support' @@ -63,6 +65,35 @@ RSpec.describe Captain::Tools::HandoffTool, type: :model do tool.perform(tool_context, reason: 'Test reason') end + it 'creates an activity message for the handoff' do + expect do + perform_enqueued_jobs do + tool.perform(tool_context, reason: 'Customer needs specialized support') + end + end.to change { conversation.messages.activity.count }.by(1) + + activity_message = conversation.messages.activity.last + expect(activity_message.content).to include(assistant.name) + expect(activity_message.message_type).to eq('activity') + expect(activity_message.private).to be false + end + + it 'sets conversation status to open for handoff' do + conversation.update(status: :pending) + expect(conversation.pending?).to be true + + tool.perform(tool_context, reason: 'Customer needs specialized support') + + conversation.reload + expect(conversation.open?).to be true + end + + it 'cleans up Current.executed_by after handoff' do + tool.perform(tool_context, reason: 'Test reason') + + expect(Current.executed_by).to be_nil + end + it 'logs tool usage with reason' do reason = 'Customer needs help' expect(tool).to receive(:log_tool_usage).with( @@ -75,6 +106,8 @@ RSpec.describe Captain::Tools::HandoffTool, type: :model do end context 'without reason provided' do + before { conversation.update!(status: :pending) } + it 'creates a private note with nil content and hands off conversation' do expect do result = tool.perform(tool_context) @@ -93,6 +126,18 @@ RSpec.describe Captain::Tools::HandoffTool, type: :model do tool.perform(tool_context) end + + it 'creates an activity message without explicit reason' do + expect do + perform_enqueued_jobs do + tool.perform(tool_context) + end + end.to change { conversation.messages.activity.count }.by(1) + + activity_message = conversation.messages.activity.last + expect(activity_message.content).to include(assistant.name) + expect(activity_message.message_type).to eq('activity') + end end context 'when handoff fails' do @@ -121,6 +166,12 @@ RSpec.describe Captain::Tools::HandoffTool, type: :model do tool.perform(tool_context, reason: 'Test') end + + it 'cleans up Current.executed_by even on error' do + tool.perform(tool_context, reason: 'Test') + + expect(Current.executed_by).to be_nil + end end end