diff --git a/lib/crewai/src/crewai/crew.py b/lib/crewai/src/crewai/crew.py index cadd9d3b1..94fe6426d 100644 --- a/lib/crewai/src/crewai/crew.py +++ b/lib/crewai/src/crewai/crew.py @@ -1122,7 +1122,7 @@ class Crew(FlowTrackable, BaseModel): ) -> list[BaseTool]: if self.manager_agent: if task.agent: - tools = self._inject_delegation_tools(tools, task.agent, [task.agent]) + tools = self._inject_delegation_tools(tools, task.agent, self.agents) else: tools = self._inject_delegation_tools( tools, self.manager_agent, self.agents diff --git a/lib/crewai/tests/cassettes/test_hierarchical_crew_creation_tasks_with_async_execution.yaml b/lib/crewai/tests/cassettes/test_hierarchical_crew_creation_tasks_with_async_execution.yaml index c7bd4a8a7..ef44352a0 100644 --- a/lib/crewai/tests/cassettes/test_hierarchical_crew_creation_tasks_with_async_execution.yaml +++ b/lib/crewai/tests/cassettes/test_hierarchical_crew_creation_tasks_with_async_execution.yaml @@ -517,4 +517,103 @@ interactions: - req_8a1b3f7a05a8699dfb93ea93be9788f9 http_version: HTTP/1.1 status_code: 200 +- request: + body: '{"trace_id": "2a3bfdec-b59a-4ab1-8be2-3c07dd2457d9", "execution_type": + "crew", "user_identifier": null, "execution_context": {"crew_fingerprint": null, + "crew_name": "crew", "flow_name": null, "crewai_version": "1.4.1", "privacy_level": + "standard"}, "execution_metadata": {"expected_duration_estimate": 300, "agent_count": + 0, "task_count": 0, "flow_method_count": 0, "execution_started_at": "2025-11-11T11:04:11.109473+00:00"}, + "ephemeral_trace_id": "2a3bfdec-b59a-4ab1-8be2-3c07dd2457d9"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '488' + Content-Type: + - application/json + User-Agent: + - CrewAI-CLI/1.4.1 + X-Crewai-Version: + - 1.4.1 + method: POST + uri: https://app.crewai.com/crewai_plus/api/v1/tracing/ephemeral/batches + response: + body: + string: '{"id":"ed5f3778-b549-4c32-b7b4-1c43eedb314e","ephemeral_trace_id":"2a3bfdec-b59a-4ab1-8be2-3c07dd2457d9","execution_type":"crew","crew_name":"crew","flow_name":null,"status":"running","duration_ms":null,"crewai_version":"1.4.1","total_events":0,"execution_context":{"crew_fingerprint":null,"crew_name":"crew","flow_name":null,"crewai_version":"1.4.1","privacy_level":"standard"},"created_at":"2025-11-11T11:04:11.451Z","updated_at":"2025-11-11T11:04:11.451Z","access_code":"TRACE-72181a84cb","user_identifier":null}' + headers: + Connection: + - keep-alive + Content-Length: + - '515' + Content-Type: + - application/json; charset=utf-8 + Date: + - Tue, 11 Nov 2025 11:04:11 GMT + cache-control: + - no-store + content-security-policy: + - 'default-src ''self'' *.app.crewai.com app.crewai.com; script-src ''self'' + ''unsafe-inline'' *.app.crewai.com app.crewai.com https://cdn.jsdelivr.net/npm/apexcharts + https://www.gstatic.com https://run.pstmn.io https://apis.google.com https://apis.google.com/js/api.js + https://accounts.google.com https://accounts.google.com/gsi/client https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css.map + https://*.google.com https://docs.google.com https://slides.google.com https://js.hs-scripts.com + https://js.sentry-cdn.com https://browser.sentry-cdn.com https://www.googletagmanager.com + https://js-na1.hs-scripts.com https://js.hubspot.com http://js-na1.hs-scripts.com + https://bat.bing.com https://cdn.amplitude.com https://cdn.segment.com https://d1d3n03t5zntha.cloudfront.net/ + https://descriptusercontent.com https://edge.fullstory.com https://googleads.g.doubleclick.net + https://js.hs-analytics.net https://js.hs-banner.com https://js.hsadspixel.net + https://js.hscollectedforms.net https://js.usemessages.com https://snap.licdn.com + https://static.cloudflareinsights.com https://static.reo.dev https://www.google-analytics.com + https://share.descript.com/; style-src ''self'' ''unsafe-inline'' *.app.crewai.com + app.crewai.com https://cdn.jsdelivr.net/npm/apexcharts; img-src ''self'' data: + *.app.crewai.com app.crewai.com https://zeus.tools.crewai.com https://dashboard.tools.crewai.com + https://cdn.jsdelivr.net https://forms.hsforms.com https://track.hubspot.com + https://px.ads.linkedin.com https://px4.ads.linkedin.com https://www.google.com + https://www.google.com.br; font-src ''self'' data: *.app.crewai.com app.crewai.com; + connect-src ''self'' *.app.crewai.com app.crewai.com https://zeus.tools.crewai.com + https://connect.useparagon.com/ https://zeus.useparagon.com/* https://*.useparagon.com/* + https://run.pstmn.io https://connect.tools.crewai.com/ https://*.sentry.io + https://www.google-analytics.com https://edge.fullstory.com https://rs.fullstory.com + https://api.hubspot.com https://forms.hscollectedforms.net https://api.hubapi.com + https://px.ads.linkedin.com https://px4.ads.linkedin.com https://google.com/pagead/form-data/16713662509 + https://google.com/ccm/form-data/16713662509 https://www.google.com/ccm/collect + https://worker-actionkit.tools.crewai.com https://api.reo.dev; frame-src ''self'' + *.app.crewai.com app.crewai.com https://connect.useparagon.com/ https://zeus.tools.crewai.com + https://zeus.useparagon.com/* https://connect.tools.crewai.com/ https://docs.google.com + https://drive.google.com https://slides.google.com https://accounts.google.com + https://*.google.com https://app.hubspot.com/ https://td.doubleclick.net https://www.googletagmanager.com/ + https://www.youtube.com https://share.descript.com' + etag: + - W/"0108bfa1b1646d9b56d1c8bfafae33c3" + expires: + - '0' + permissions-policy: + - camera=(), microphone=(self), geolocation=() + pragma: + - no-cache + referrer-policy: + - strict-origin-when-cross-origin + strict-transport-security: + - max-age=63072000; includeSubDomains + vary: + - Accept + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + x-permitted-cross-domain-policies: + - none + x-request-id: + - 2bc7f5a5-7082-4864-8f9e-dafe6bff32b5 + x-runtime: + - '0.070395' + x-xss-protection: + - 1; mode=block + status: + code: 201 + message: Created version: 1 diff --git a/lib/crewai/tests/test_crew.py b/lib/crewai/tests/test_crew.py index d4cf1acbf..3e1fb0bcd 100644 --- a/lib/crewai/tests/test_crew.py +++ b/lib/crewai/tests/test_crew.py @@ -1864,7 +1864,10 @@ def test_hierarchical_crew_creation_tasks_with_agents(researcher, writer): @pytest.mark.vcr(filter_headers=["authorization"]) def test_hierarchical_crew_creation_tasks_with_async_execution(researcher, writer, ceo): """ - Tests that async tasks in hierarchical crews are handled correctly with proper delegation tools + Tests that async tasks in hierarchical crews are handled correctly with proper delegation tools. + + After fix for issue #3887, the manager should be able to delegate to ALL crew agents, + not just the task's assigned agent. """ task = Task( description="Write one amazing paragraph about AI.", @@ -1906,13 +1909,15 @@ def test_hierarchical_crew_creation_tasks_with_async_execution(researcher, write # Verify the delegation tools were passed correctly assert len(tools) == 2 + + # After fix for issue #3887, manager can delegate to ALL crew agents assert any( - "Delegate a specific task to one of the following coworkers: Senior Writer\n" + "Delegate a specific task to one of the following coworkers: Senior Writer, Researcher, CEO" in tool.description for tool in tools ) assert any( - "Ask a specific question to one of the following coworkers: Senior Writer\n" + "Ask a specific question to one of the following coworkers: Senior Writer, Researcher, CEO" in tool.description for tool in tools ) @@ -4772,3 +4777,186 @@ def test_ensure_exchanged_messages_are_propagated_to_external_memory(): assert "Researcher" in messages[0]["content"] assert messages[1]["role"] == "user" assert "Research a topic to teach a kid aged 6 about math" in messages[1]["content"] + + +def test_hierarchical_manager_can_delegate_to_crew_agents_issue_3887(): + """ + Test that reproduces and fixes issue #3887. + + When using hierarchical process with a custom manager_agent and tasks assigned + to that manager, the manager should be able to delegate to all agents in the + crew's agents list, not just the task's assigned agent. + + Bug scenario from issue #3887: + - Crew has agents=[planner] + - manager_agent=coordinator + - Task is assigned to coordinator (the manager) + - Manager tries to delegate to "planner" + - Before fix: Error "coworker mentioned not found, it must be one of the following options: - coordinator" + - After fix: Manager can successfully delegate to "planner" + """ + # Create agents matching the bug report scenario + coordinator = Agent( + role="Coordinator", + goal="Coordinate the team to complete tasks", + backstory="You're a coordinator who delegates work to team members.", + allow_delegation=True, + ) + + planner = Agent( + role="Planner", + goal="Create technical plans", + backstory="You're a planner who creates detailed technical plans.", + allow_delegation=False, + ) + + # Create a task assigned to the coordinator (manager agent) + task = Task( + description="Create a technical plan for the project", + expected_output="A detailed technical plan", + agent=coordinator, + ) + + # Create hierarchical crew with custom manager_agent + crew = Crew( + agents=[planner], # Only planner in crew agents + tasks=[task], + process=Process.hierarchical, + manager_agent=coordinator, # Coordinator is the manager + ) + + mock_task_output = TaskOutput( + description="Mock description", + raw="mocked output", + agent="mocked agent", + messages=[] + ) + task.output = mock_task_output + + with patch.object( + Task, "execute_sync", return_value=mock_task_output + ) as mock_execute_sync: + crew.kickoff() + + # Verify execute_sync was called + mock_execute_sync.assert_called_once() + + # Get the tools argument from the call + _, kwargs = mock_execute_sync.call_args + tools = kwargs["tools"] + + # Verify the delegation tools were passed correctly + # The manager should be able to delegate to "planner" (from crew.agents) + assert len(tools) == 2 + + # Check that the delegation tool includes "Planner" as a coworker + delegation_tool = next( + (tool for tool in tools if "Delegate" in tool.name), None + ) + assert delegation_tool is not None + assert "Planner" in delegation_tool.description, ( + f"Expected 'Planner' in delegation tool description, " + f"but got: {delegation_tool.description}" + ) + + # Verify the delegation tool has the planner agent in its agents list + assert hasattr(delegation_tool, "agents") + agent_roles = [agent.role for agent in delegation_tool.agents] + assert "Planner" in agent_roles, ( + f"Expected 'Planner' in delegation tool agents, " + f"but got: {agent_roles}" + ) + + # Verify that "Coordinator" is NOT in the coworkers list + # (manager should not delegate to itself) + assert "Coordinator" not in delegation_tool.description or ( + "Coordinator" in delegation_tool.description and "Planner" in delegation_tool.description + ), "Manager should be able to delegate to crew agents" + + +def test_hierarchical_manager_can_delegate_to_multiple_crew_agents(): + """ + Test that the manager can delegate to multiple crew agents. + + This extends the fix for issue #3887 to ensure it works with multiple agents. + """ + # Create manager agent + manager = Agent( + role="Manager", + goal="Manage the team", + backstory="You're a manager who coordinates team members.", + allow_delegation=True, + ) + + # Create multiple crew agents + researcher = Agent( + role="Researcher", + goal="Research topics", + backstory="You're a researcher.", + allow_delegation=False, + ) + + writer = Agent( + role="Writer", + goal="Write content", + backstory="You're a writer.", + allow_delegation=False, + ) + + analyst = Agent( + role="Analyst", + goal="Analyze data", + backstory="You're an analyst.", + allow_delegation=False, + ) + + # Create a task assigned to the manager + task = Task( + description="Coordinate the team to complete the project", + expected_output="Project completed", + agent=manager, + ) + + # Create hierarchical crew with multiple agents + crew = Crew( + agents=[researcher, writer, analyst], + tasks=[task], + process=Process.hierarchical, + manager_agent=manager, + ) + + mock_task_output = TaskOutput( + description="Mock description", + raw="mocked output", + agent="mocked agent", + messages=[] + ) + task.output = mock_task_output + + with patch.object( + Task, "execute_sync", return_value=mock_task_output + ) as mock_execute_sync: + crew.kickoff() + + # Get the tools argument from the call + _, kwargs = mock_execute_sync.call_args + tools = kwargs["tools"] + + # Find the delegation tool + delegation_tool = next( + (tool for tool in tools if "Delegate" in tool.name), None + ) + assert delegation_tool is not None + + # Verify all crew agents are available for delegation + assert hasattr(delegation_tool, "agents") + agent_roles = [agent.role for agent in delegation_tool.agents] + + assert "Researcher" in agent_roles + assert "Writer" in agent_roles + assert "Analyst" in agent_roles + + # Verify all agents are mentioned in the description + assert "Researcher" in delegation_tool.description + assert "Writer" in delegation_tool.description + assert "Analyst" in delegation_tool.description