diff --git a/src/crewai/security/encrypted_communication.py b/src/crewai/security/encrypted_communication.py index afb8466e1..8c2e86907 100644 --- a/src/crewai/security/encrypted_communication.py +++ b/src/crewai/security/encrypted_communication.py @@ -14,6 +14,13 @@ from cryptography.fernet import Fernet from pydantic import BaseModel, Field from crewai.security.fingerprint import Fingerprint +from crewai.utilities.events.crewai_event_bus import crewai_event_bus +from crewai.utilities.events.encryption_events import ( + EncryptionStartedEvent, + EncryptionCompletedEvent, + DecryptionStartedEvent, + DecryptionCompletedEvent, +) logger = logging.getLogger(__name__) @@ -42,14 +49,16 @@ class AgentCommunicationEncryption: Provides methods to encrypt and decrypt communication payloads. """ - def __init__(self, agent_fingerprint: Fingerprint): + def __init__(self, agent_fingerprint: Fingerprint, agent=None): """ Initialize encryption handler for an agent. Args: agent_fingerprint (Fingerprint): The agent's unique fingerprint + agent: The agent instance (optional, needed for events) """ self.agent_fingerprint = agent_fingerprint + self.agent = agent self._encryption_keys: Dict[str, Fernet] = {} def _derive_communication_key(self, sender_fp: str, recipient_fp: str) -> bytes: @@ -102,7 +111,8 @@ class AgentCommunicationEncryption: self, message: Union[str, Dict[str, Any]], recipient_fingerprint: Fingerprint, - message_type: str = "communication" + message_type: str = "communication", + recipient_agent=None ) -> EncryptedMessage: """ Encrypt a message for a specific recipient agent. @@ -111,6 +121,7 @@ class AgentCommunicationEncryption: message (Union[str, Dict[str, Any]]): The message to encrypt recipient_fingerprint (Fingerprint): The recipient agent's fingerprint message_type (str): Type of message being sent + recipient_agent: The recipient agent instance (optional, needed for events) Returns: EncryptedMessage: Encrypted message container @@ -119,6 +130,17 @@ class AgentCommunicationEncryption: ValueError: If encryption fails """ try: + # Emit encryption started event if both agents are available + if self.agent and recipient_agent: + crewai_event_bus.emit( + self.agent, + EncryptionStartedEvent( + sender_agent=self.agent, + recipient_agent=recipient_agent, + message_type=message_type + ) + ) + logger.info(f"Starting encryption for {message_type} message to recipient {recipient_fingerprint.uuid_str[:8]}...") # Convert message to JSON string if it's a dict @@ -137,6 +159,17 @@ class AgentCommunicationEncryption: encrypted_bytes = fernet.encrypt(message_str.encode('utf-8')) encrypted_payload = encrypted_bytes.decode('utf-8') + # Emit encryption completed event if both agents are available + if self.agent and recipient_agent: + crewai_event_bus.emit( + self.agent, + EncryptionCompletedEvent( + sender_agent=self.agent, + recipient_agent=recipient_agent, + message_type=message_type + ) + ) + logger.info(f"Successfully encrypted {message_type} message from {self.agent_fingerprint.uuid_str[:8]}... to {recipient_fingerprint.uuid_str[:8]}...") logger.debug(f"Encrypted message from {self.agent_fingerprint.uuid_str[:8]}... to {recipient_fingerprint.uuid_str[:8]}...") @@ -165,6 +198,17 @@ class AgentCommunicationEncryption: ValueError: If decryption fails or message is not for this agent """ try: + # Emit decryption started event if agent is available + if self.agent: + crewai_event_bus.emit( + self.agent, + DecryptionStartedEvent( + recipient_agent=self.agent, + sender_fingerprint=encrypted_message.sender_fingerprint, + message_type=encrypted_message.message_type + ) + ) + logger.info(f"Starting decryption of {encrypted_message.message_type} message from sender {encrypted_message.sender_fingerprint[:8]}...") # Verify this message is intended for this agent @@ -186,6 +230,17 @@ class AgentCommunicationEncryption: decrypted_content = json.loads(decrypted_str) except json.JSONDecodeError: decrypted_content = decrypted_str + + # Emit decryption completed event if agent is available + if self.agent: + crewai_event_bus.emit( + self.agent, + DecryptionCompletedEvent( + recipient_agent=self.agent, + sender_fingerprint=encrypted_message.sender_fingerprint, + message_type=encrypted_message.message_type + ) + ) logger.info(f"Successfully decrypted {encrypted_message.message_type} message from {encrypted_message.sender_fingerprint[:8]}... to {encrypted_message.recipient_fingerprint[:8]}...") diff --git a/src/crewai/tools/agent_tools/base_agent_tools.py b/src/crewai/tools/agent_tools/base_agent_tools.py index 7a27340a8..873b1b837 100644 --- a/src/crewai/tools/agent_tools/base_agent_tools.py +++ b/src/crewai/tools/agent_tools/base_agent_tools.py @@ -8,6 +8,12 @@ from crewai.task import Task from crewai.tools.base_tool import BaseTool from crewai.utilities import I18N from crewai.security import AgentCommunicationEncryption, EncryptedMessage +from crewai.utilities.events.crewai_event_bus import crewai_event_bus +from crewai.utilities.events.encryption_events import ( + EncryptedCommunicationStartedEvent, + EncryptedCommunicationEstablishedEvent, + EncryptedTaskExecutionEvent, +) logger = logging.getLogger(__name__) @@ -44,10 +50,11 @@ class BaseAgentTool(BaseTool): if not getattr(sender_agent.security_config, 'encrypted_communication', False): return None - # Create encryption handler if it doesn't exist + # Create encryption handler if it doesn't exist, passing the agent instance if self._encryption_handler is None: self._encryption_handler = AgentCommunicationEncryption( - sender_agent.security_config.fingerprint + sender_agent.security_config.fingerprint, + agent=sender_agent ) return self._encryption_handler @@ -83,13 +90,33 @@ class BaseAgentTool(BaseTool): encryption_handler = self._get_encryption_handler(sender_agent) if encryption_handler and hasattr(recipient_agent, 'security_config') and recipient_agent.security_config: try: + # Emit communication started event + crewai_event_bus.emit( + sender_agent, + EncryptedCommunicationStartedEvent( + sender_agent=sender_agent, + recipient_agent=recipient_agent + ) + ) + logger.info(f"Starting encrypted communication from '{sender_agent.role}' to '{recipient_agent.role}'") # Encrypt the message for the recipient encrypted_msg = encryption_handler.encrypt_message( message_payload, recipient_agent.security_config.fingerprint, - message_type="agent_communication" + message_type="agent_communication", + recipient_agent=recipient_agent ) + + # Emit communication established event + crewai_event_bus.emit( + sender_agent, + EncryptedCommunicationEstablishedEvent( + sender_agent=sender_agent, + recipient_agent=recipient_agent + ) + ) + logger.info(f"Encrypted communication established between '{sender_agent.role}' and '{recipient_agent.role}'") logger.debug(f"Encrypted communication from {sender_agent.role} to {recipient_agent.role}") return encrypted_msg @@ -117,15 +144,23 @@ class BaseAgentTool(BaseTool): if isinstance(message, EncryptedMessage) or ( isinstance(message, dict) and 'encrypted_payload' in message ): - encryption_handler = self._get_encryption_handler(recipient_agent) - if encryption_handler: + # We need an encryption handler for the recipient agent + recipient_encryption_handler = None + if hasattr(recipient_agent, 'security_config') and recipient_agent.security_config: + if getattr(recipient_agent.security_config, 'encrypted_communication', False): + recipient_encryption_handler = AgentCommunicationEncryption( + recipient_agent.security_config.fingerprint, + agent=recipient_agent + ) + + if recipient_encryption_handler: try: logger.info(f"Starting decryption of received communication for '{recipient_agent.role}'") # Convert dict to EncryptedMessage if needed if isinstance(message, dict): message = EncryptedMessage(**message) - decrypted = encryption_handler.decrypt_message(message) + decrypted = recipient_encryption_handler.decrypt_message(message) logger.info(f"Successfully decrypted communication for '{recipient_agent.role}'") logger.debug(f"Decrypted communication for {recipient_agent.role}") return decrypted @@ -257,6 +292,14 @@ class BaseAgentTool(BaseTool): # Execute with processed communication context if isinstance(communication_payload, EncryptedMessage): + # Emit encrypted task execution event + crewai_event_bus.emit( + target_agent, + EncryptedTaskExecutionEvent( + agent=target_agent + ) + ) + logger.info(f"Executing encrypted communication task for agent '{self.sanitize_agent_name(target_agent.role)}'") logger.debug(f"Executing encrypted communication task for agent '{self.sanitize_agent_name(target_agent.role)}'") # For encrypted messages, pass the encrypted payload as additional context diff --git a/src/crewai/utilities/events/encryption_events.py b/src/crewai/utilities/events/encryption_events.py new file mode 100644 index 000000000..eb02be2ed --- /dev/null +++ b/src/crewai/utilities/events/encryption_events.py @@ -0,0 +1,165 @@ +""" +Encryption events for agent-to-agent communication +""" + +from typing import Optional +from crewai.agents.agent_builder.base_agent import BaseAgent +from .base_events import BaseEvent + + +class EncryptionStartedEvent(BaseEvent): + """Event emitted when agent-to-agent encryption starts""" + + sender_agent: BaseAgent + recipient_agent: BaseAgent + message_type: str = "agent_communication" + type: str = "encryption_started" + + model_config = {"arbitrary_types_allowed": True} + + def __init__(self, **data): + super().__init__(**data) + # Set fingerprint data from the sender agent + if hasattr(self.sender_agent, "fingerprint") and self.sender_agent.fingerprint: + self.source_fingerprint = self.sender_agent.fingerprint.uuid_str + self.source_type = "agent" + if ( + hasattr(self.sender_agent.fingerprint, "metadata") + and self.sender_agent.fingerprint.metadata + ): + self.fingerprint_metadata = self.sender_agent.fingerprint.metadata + + +class EncryptionCompletedEvent(BaseEvent): + """Event emitted when agent-to-agent encryption completes successfully""" + + sender_agent: BaseAgent + recipient_agent: BaseAgent + message_type: str = "agent_communication" + type: str = "encryption_completed" + + model_config = {"arbitrary_types_allowed": True} + + def __init__(self, **data): + super().__init__(**data) + # Set fingerprint data from the sender agent + if hasattr(self.sender_agent, "fingerprint") and self.sender_agent.fingerprint: + self.source_fingerprint = self.sender_agent.fingerprint.uuid_str + self.source_type = "agent" + if ( + hasattr(self.sender_agent.fingerprint, "metadata") + and self.sender_agent.fingerprint.metadata + ): + self.fingerprint_metadata = self.sender_agent.fingerprint.metadata + + +class DecryptionStartedEvent(BaseEvent): + """Event emitted when agent-to-agent decryption starts""" + + recipient_agent: BaseAgent + sender_fingerprint: str + message_type: str = "agent_communication" + type: str = "decryption_started" + + model_config = {"arbitrary_types_allowed": True} + + def __init__(self, **data): + super().__init__(**data) + # Set fingerprint data from the recipient agent + if hasattr(self.recipient_agent, "fingerprint") and self.recipient_agent.fingerprint: + self.source_fingerprint = self.recipient_agent.fingerprint.uuid_str + self.source_type = "agent" + if ( + hasattr(self.recipient_agent.fingerprint, "metadata") + and self.recipient_agent.fingerprint.metadata + ): + self.fingerprint_metadata = self.recipient_agent.fingerprint.metadata + + +class DecryptionCompletedEvent(BaseEvent): + """Event emitted when agent-to-agent decryption completes successfully""" + + recipient_agent: BaseAgent + sender_fingerprint: str + message_type: str = "agent_communication" + type: str = "decryption_completed" + + model_config = {"arbitrary_types_allowed": True} + + def __init__(self, **data): + super().__init__(**data) + # Set fingerprint data from the recipient agent + if hasattr(self.recipient_agent, "fingerprint") and self.recipient_agent.fingerprint: + self.source_fingerprint = self.recipient_agent.fingerprint.uuid_str + self.source_type = "agent" + if ( + hasattr(self.recipient_agent.fingerprint, "metadata") + and self.recipient_agent.fingerprint.metadata + ): + self.fingerprint_metadata = self.recipient_agent.fingerprint.metadata + + +class EncryptedCommunicationStartedEvent(BaseEvent): + """Event emitted when encrypted communication between agents begins""" + + sender_agent: BaseAgent + recipient_agent: BaseAgent + type: str = "encrypted_communication_started" + + model_config = {"arbitrary_types_allowed": True} + + def __init__(self, **data): + super().__init__(**data) + # Set fingerprint data from the sender agent + if hasattr(self.sender_agent, "fingerprint") and self.sender_agent.fingerprint: + self.source_fingerprint = self.sender_agent.fingerprint.uuid_str + self.source_type = "agent" + if ( + hasattr(self.sender_agent.fingerprint, "metadata") + and self.sender_agent.fingerprint.metadata + ): + self.fingerprint_metadata = self.sender_agent.fingerprint.metadata + + +class EncryptedCommunicationEstablishedEvent(BaseEvent): + """Event emitted when encrypted communication is successfully established""" + + sender_agent: BaseAgent + recipient_agent: BaseAgent + type: str = "encrypted_communication_established" + + model_config = {"arbitrary_types_allowed": True} + + def __init__(self, **data): + super().__init__(**data) + # Set fingerprint data from the sender agent + if hasattr(self.sender_agent, "fingerprint") and self.sender_agent.fingerprint: + self.source_fingerprint = self.sender_agent.fingerprint.uuid_str + self.source_type = "agent" + if ( + hasattr(self.sender_agent.fingerprint, "metadata") + and self.sender_agent.fingerprint.metadata + ): + self.fingerprint_metadata = self.sender_agent.fingerprint.metadata + + +class EncryptedTaskExecutionEvent(BaseEvent): + """Event emitted when an encrypted communication task is being executed""" + + agent: BaseAgent + task_type: str = "encrypted_communication" + type: str = "encrypted_task_execution" + + model_config = {"arbitrary_types_allowed": True} + + def __init__(self, **data): + super().__init__(**data) + # Set fingerprint data from the agent + if hasattr(self.agent, "fingerprint") and self.agent.fingerprint: + self.source_fingerprint = self.agent.fingerprint.uuid_str + self.source_type = "agent" + if ( + hasattr(self.agent.fingerprint, "metadata") + and self.agent.fingerprint.metadata + ): + self.fingerprint_metadata = self.agent.fingerprint.metadata \ No newline at end of file diff --git a/src/crewai/utilities/events/event_listener.py b/src/crewai/utilities/events/event_listener.py index 1917eca1f..f5423dbd7 100644 --- a/src/crewai/utilities/events/event_listener.py +++ b/src/crewai/utilities/events/event_listener.py @@ -37,6 +37,15 @@ from .agent_events import ( LiteAgentExecutionErrorEvent, LiteAgentExecutionStartedEvent, ) +from .encryption_events import ( + EncryptionStartedEvent, + EncryptionCompletedEvent, + DecryptionStartedEvent, + DecryptionCompletedEvent, + EncryptedCommunicationStartedEvent, + EncryptedCommunicationEstablishedEvent, + EncryptedTaskExecutionEvent, +) from .crew_events import ( CrewKickoffCompletedEvent, CrewKickoffFailedEvent, @@ -513,5 +522,70 @@ class EventListener(BaseEventListener): event.verbose, ) + # Encryption event handlers + @crewai_event_bus.on(EncryptedCommunicationStartedEvent) + def on_encrypted_communication_started(source, event: EncryptedCommunicationStartedEvent): + self.formatter.handle_encryption_communication_started( + event.sender_agent.role, + event.recipient_agent.role + ) + + @crewai_event_bus.on(EncryptedCommunicationEstablishedEvent) + def on_encrypted_communication_established(source, event: EncryptedCommunicationEstablishedEvent): + self.formatter.handle_encryption_communication_established( + event.sender_agent.role, + event.recipient_agent.role + ) + + @crewai_event_bus.on(EncryptionStartedEvent) + def on_encryption_started(source, event: EncryptionStartedEvent): + recipient_fingerprint = "" + if hasattr(event.recipient_agent, "fingerprint") and event.recipient_agent.fingerprint: + recipient_fingerprint = event.recipient_agent.fingerprint.uuid_str[:8] + "..." + self.formatter.handle_encryption_started( + event.message_type, + recipient_fingerprint + ) + + @crewai_event_bus.on(EncryptionCompletedEvent) + def on_encryption_completed(source, event: EncryptionCompletedEvent): + sender_fingerprint = "" + recipient_fingerprint = "" + if hasattr(event.sender_agent, "fingerprint") and event.sender_agent.fingerprint: + sender_fingerprint = event.sender_agent.fingerprint.uuid_str[:8] + "..." + if hasattr(event.recipient_agent, "fingerprint") and event.recipient_agent.fingerprint: + recipient_fingerprint = event.recipient_agent.fingerprint.uuid_str[:8] + "..." + self.formatter.handle_encryption_completed( + event.message_type, + sender_fingerprint, + recipient_fingerprint + ) + + @crewai_event_bus.on(DecryptionStartedEvent) + def on_decryption_started(source, event: DecryptionStartedEvent): + sender_fingerprint = event.sender_fingerprint[:8] + "..." if event.sender_fingerprint else "" + self.formatter.handle_decryption_started( + event.message_type, + sender_fingerprint + ) + + @crewai_event_bus.on(DecryptionCompletedEvent) + def on_decryption_completed(source, event: DecryptionCompletedEvent): + sender_fingerprint = event.sender_fingerprint[:8] + "..." if event.sender_fingerprint else "" + recipient_fingerprint = "" + if hasattr(event.recipient_agent, "fingerprint") and event.recipient_agent.fingerprint: + recipient_fingerprint = event.recipient_agent.fingerprint.uuid_str[:8] + "..." + self.formatter.handle_decryption_completed( + event.message_type, + sender_fingerprint, + recipient_fingerprint + ) + + @crewai_event_bus.on(EncryptedTaskExecutionEvent) + def on_encrypted_task_execution(source, event: EncryptedTaskExecutionEvent): + self.formatter.handle_encrypted_task_execution( + event.agent.role + ) + event_listener = EventListener() diff --git a/src/crewai/utilities/events/utils/console_formatter.py b/src/crewai/utilities/events/utils/console_formatter.py index 5039aa1be..064c1f063 100644 --- a/src/crewai/utilities/events/utils/console_formatter.py +++ b/src/crewai/utilities/events/utils/console_formatter.py @@ -1754,3 +1754,151 @@ class ConsoleFormatter: Attempts=f"{retry_count + 1}", ) self.print_panel(content, "🛡️ Guardrail Failed", "red") + + # Encryption event handlers + + def handle_encryption_communication_started( + self, sender_role: str, recipient_role: str + ) -> None: + """Handle encrypted communication started event.""" + if not self.verbose: + return + + content = Text() + content.append("Starting encrypted communication from '", style="white") + content.append(f"{sender_role}", style="bright_cyan bold") + content.append("' to '", style="white") + content.append(f"{recipient_role}", style="bright_cyan bold") + content.append("'", style="white") + + panel = Panel( + content, + title="🔐 Encrypted Communication", + border_style="cyan", + padding=(0, 1), + ) + self.print(panel) + + def handle_encryption_communication_established( + self, sender_role: str, recipient_role: str + ) -> None: + """Handle encrypted communication established event.""" + if not self.verbose: + return + + content = Text() + content.append("Encrypted communication established between '", style="white") + content.append(f"{sender_role}", style="bright_green bold") + content.append("' and '", style="white") + content.append(f"{recipient_role}", style="bright_green bold") + content.append("'", style="white") + + panel = Panel( + content, + title="✅ Communication Secured", + border_style="green", + padding=(0, 1), + ) + self.print(panel) + + def handle_encryption_started( + self, message_type: str, recipient_fingerprint: str + ) -> None: + """Handle encryption started event.""" + if not self.verbose: + return + + content = Text() + content.append(f"Starting encryption for {message_type} message to recipient ", style="white") + content.append(f"{recipient_fingerprint}", style="bright_yellow") + content.append("...", style="white") + + panel = Panel( + content, + title="🔒 Encrypting Message", + border_style="yellow", + padding=(0, 1), + ) + self.print(panel) + + def handle_encryption_completed( + self, message_type: str, sender_fingerprint: str, recipient_fingerprint: str + ) -> None: + """Handle encryption completed event.""" + if not self.verbose: + return + + content = Text() + content.append(f"Successfully encrypted {message_type} message from ", style="white") + content.append(f"{sender_fingerprint}", style="bright_green") + content.append(" to ", style="white") + content.append(f"{recipient_fingerprint}", style="bright_green") + content.append("...", style="white") + + panel = Panel( + content, + title="✅ Message Encrypted", + border_style="green", + padding=(0, 1), + ) + self.print(panel) + + def handle_decryption_started( + self, message_type: str, sender_fingerprint: str + ) -> None: + """Handle decryption started event.""" + if not self.verbose: + return + + content = Text() + content.append(f"Starting decryption of {message_type} message from sender ", style="white") + content.append(f"{sender_fingerprint}", style="bright_yellow") + content.append("...", style="white") + + panel = Panel( + content, + title="🔓 Decrypting Message", + border_style="yellow", + padding=(0, 1), + ) + self.print(panel) + + def handle_decryption_completed( + self, message_type: str, sender_fingerprint: str, recipient_fingerprint: str + ) -> None: + """Handle decryption completed event.""" + if not self.verbose: + return + + content = Text() + content.append(f"Successfully decrypted {message_type} message from ", style="white") + content.append(f"{sender_fingerprint}", style="bright_green") + content.append(" to ", style="white") + content.append(f"{recipient_fingerprint}", style="bright_green") + content.append("...", style="white") + + panel = Panel( + content, + title="✅ Message Decrypted", + border_style="green", + padding=(0, 1), + ) + self.print(panel) + + def handle_encrypted_task_execution(self, agent_role: str) -> None: + """Handle encrypted task execution event.""" + if not self.verbose: + return + + content = Text() + content.append("Executing encrypted communication task for agent '", style="white") + content.append(f"{agent_role}", style="bright_blue bold") + content.append("'", style="white") + + panel = Panel( + content, + title="🔐 Executing Encrypted Task", + border_style="blue", + padding=(0, 1), + ) + self.print(panel)