diff --git a/src/crewai/task.py b/src/crewai/task.py index ebf2843179..9c48eda1b6 100644 --- a/src/crewai/task.py +++ b/src/crewai/task.py @@ -336,12 +336,6 @@ def set_attributes_based_on_config(self) -> "Task": setattr(self, key, value) return self - @model_validator(mode="after") - def check_tools(self): - """Check if the tools are set.""" - if not self.tools and self.agent and self.agent.tools: - self.tools.extend(self.agent.tools) - return self @model_validator(mode="after") def check_output(self): diff --git a/tests/test_crew.py b/tests/test_crew.py index 0a9b946952..661062dbb0 100644 --- a/tests/test_crew.py +++ b/tests/test_crew.py @@ -200,7 +200,7 @@ def test_async_task_cannot_include_sequential_async_tasks_in_context( # This should raise an error because task2 is async and has task1 in its context without a sync task in between with pytest.raises( ValueError, - match="Task 'Task 2' is asynchronous and cannot include other sequential asynchronous tasks in its context.", + match=r"Task 'Task 2' is asynchronous and cannot include other sequential asynchronous tasks in its context.", ): Crew(tasks=[task1, task2, task3, task4, task5], agents=[researcher, writer]) @@ -238,7 +238,7 @@ def test_context_no_future_tasks(researcher, writer): # This should raise an error because task1 has a context dependency on a future task (task4) with pytest.raises( ValueError, - match="Task 'Task 1' has a context dependency on a future task 'Task 4', which is not allowed.", + match=r"Task 'Task 1' has a context dependency on a future task 'Task 4', which is not allowed.", ): Crew(tasks=[task1, task2, task3, task4], agents=[researcher, writer]) @@ -3339,7 +3339,7 @@ def test_replay_with_invalid_task_id(): ): with pytest.raises( ValueError, - match="Task with id bf5b09c9-69bd-4eb8-be12-f9e5bae31c2d not found in the crew's tasks.", + match=r"Task with id bf5b09c9-69bd-4eb8-be12-f9e5bae31c2d not found in the crew's tasks.", ): crew.replay("bf5b09c9-69bd-4eb8-be12-f9e5bae31c2d") @@ -3814,6 +3814,46 @@ def test_fetch_inputs(): ) +def test_hierarchical_crew_does_not_propagate_agent_tools_to_manager(): + """ + Test that in hierarchical crews, manager agent doesn't inherit task agents' tools. + This verifies that the check_tools validator doesn't pollute task.tools at creation time. + Fixes issue #3679: https://github.com/crewAIInc/crewAI/issues/3679 + """ + from crewai.tools import tool + + @tool + def agent_specific_tool() -> str: + """A tool that should only be available to the specific agent.""" + return "agent specific result" + + agent_with_tools = Agent( + role="Specialist", + goal="Do specialized work with custom tools", + backstory="You are a specialist with specific tools", + tools=[agent_specific_tool], + allow_delegation=False, + ) + + # Create a task with an agent that has tools, but don't assign tools to the task + task = Task( + description="Perform a specialized task", + expected_output="Task result", + agent=agent_with_tools, + ) + + Crew( + agents=[agent_with_tools], + tasks=[task], + process=Process.hierarchical, + manager_llm="gpt-4o", + ) + + # Verify that task.tools is empty (not populated with agent's tools) + assert task.tools == [] + assert len(task.tools) == 0 + + def test_task_tools_preserve_code_execution_tools(): """ Test that task tools don't override code execution tools when allow_code_execution=True diff --git a/tests/test_task.py b/tests/test_task.py index 0e304df540..5fff55713f 100644 --- a/tests/test_task.py +++ b/tests/test_task.py @@ -20,11 +20,13 @@ def test_task_tool_reflect_agent_tools(): + """Test that agent tools are available during task execution via crew fallback logic.""" from crewai.tools import tool @tool - def fake_tool() -> None: + def fake_tool() -> str: "Fake tool" + return "result" researcher = Agent( role="Researcher", @@ -40,7 +42,9 @@ def fake_tool() -> None: agent=researcher, ) - assert task.tools == [fake_tool] + assert task.tools == [] + + assert researcher.tools == [fake_tool] def test_task_tool_takes_precedence_over_agent_tools(): @@ -1218,7 +1222,7 @@ def test_create_directory_false(): assert not resolved_dir.exists() with pytest.raises( - RuntimeError, match="Directory .* does not exist and create_directory is False" + RuntimeError, match=r"Directory .* does not exist and create_directory is False" ): task._save_file("test content")