Co-authored-by: Quentin Torroba <quentin.torroba@mistral.ai>
Co-authored-by: Michel Thomazo <michel.thomazo@mistral.ai>
Co-authored-by: Clément Drouin <clement.drouin@mistral.ai>
Co-authored-by: Vincent Guilloux <vincent.guilloux@mistral.ai>
Co-authored-by: Clément Siriex <clement.sirieix@mistral.ai>
Co-authored-by: Kim-Adeline Miguel <kimadeline.miguel@mistral.ai>
Co-authored-by: Nicolas Karolak <nicolas@karolak.fr>
This commit is contained in:
Mathias Gesbert 2026-02-11 18:17:30 +01:00 committed by GitHub
parent 9809cfc831
commit 51fecc67d9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
176 changed files with 8652 additions and 4451 deletions

View file

@ -4,9 +4,9 @@ from pathlib import Path
import pytest
from tests.conftest import build_test_agent_loop, build_test_vibe_config
from tests.mock.utils import mock_llm_chunk
from tests.stubs.fake_backend import FakeBackend
from vibe.core.agent_loop import AgentLoop
from vibe.core.agents.manager import AgentManager
from vibe.core.agents.models import (
BUILTIN_AGENTS,
@ -17,7 +17,7 @@ from vibe.core.agents.models import (
BuiltinAgentName,
_deep_merge,
)
from vibe.core.config import SessionLoggingConfig, VibeConfig
from vibe.core.config import VibeConfig
from vibe.core.paths.config_paths import ConfigPath
from vibe.core.paths.global_paths import GlobalPath
from vibe.core.tools.base import ToolPermission
@ -231,8 +231,7 @@ class TestAgentProfileOverrides:
class TestAgentManagerCycling:
@pytest.fixture
def base_config(self) -> VibeConfig:
return VibeConfig(
session_logging=SessionLoggingConfig(enabled=False),
return build_test_vibe_config(
auto_compact_threshold=0,
include_project_context=False,
include_prompt_detail=False,
@ -250,8 +249,8 @@ class TestAgentManagerCycling:
def test_get_agent_order_includes_primary_agents(
self, base_config: VibeConfig, backend: FakeBackend
) -> None:
agent = AgentLoop(
base_config, agent_name=BuiltinAgentName.DEFAULT, backend=backend
agent = build_test_agent_loop(
config=base_config, agent_name=BuiltinAgentName.DEFAULT, backend=backend
)
order = agent.agent_manager.get_agent_order()
assert len(order) == 4
@ -263,8 +262,8 @@ class TestAgentManagerCycling:
def test_next_agent_cycles_through_all(
self, base_config: VibeConfig, backend: FakeBackend
) -> None:
agent = AgentLoop(
base_config, agent_name=BuiltinAgentName.DEFAULT, backend=backend
agent = build_test_agent_loop(
config=base_config, agent_name=BuiltinAgentName.DEFAULT, backend=backend
)
order = agent.agent_manager.get_agent_order()
current = agent.agent_manager.active_profile
@ -277,8 +276,8 @@ class TestAgentManagerCycling:
def test_next_agent_wraps_around(
self, base_config: VibeConfig, backend: FakeBackend
) -> None:
agent = AgentLoop(
base_config, agent_name=BuiltinAgentName.DEFAULT, backend=backend
agent = build_test_agent_loop(
config=base_config, agent_name=BuiltinAgentName.DEFAULT, backend=backend
)
order = agent.agent_manager.get_agent_order()
last_profile = agent.agent_manager.get_agent(order[-1])
@ -301,8 +300,7 @@ class TestAgentProfileConfig:
class TestAgentSwitchAgent:
@pytest.fixture
def base_config(self) -> VibeConfig:
return VibeConfig(
session_logging=SessionLoggingConfig(enabled=False),
return build_test_vibe_config(
auto_compact_threshold=0,
include_project_context=False,
include_prompt_detail=False,
@ -321,8 +319,8 @@ class TestAgentSwitchAgent:
async def test_switch_to_plan_agent_restricts_tools(
self, base_config: VibeConfig, backend: FakeBackend
) -> None:
agent = AgentLoop(
base_config, agent_name=BuiltinAgentName.DEFAULT, backend=backend
agent = build_test_agent_loop(
config=base_config, agent_name=BuiltinAgentName.DEFAULT, backend=backend
)
initial_tool_names = set(agent.tool_manager.available_tools.keys())
assert len(initial_tool_names) > len(PLAN_AGENT_TOOLS)
@ -337,8 +335,8 @@ class TestAgentSwitchAgent:
async def test_switch_from_plan_to_default_restores_tools(
self, base_config: VibeConfig, backend: FakeBackend
) -> None:
agent = AgentLoop(
base_config, agent_name=BuiltinAgentName.PLAN, backend=backend
agent = build_test_agent_loop(
config=base_config, agent_name=BuiltinAgentName.PLAN, backend=backend
)
assert len(agent.tool_manager.available_tools) == len(PLAN_AGENT_TOOLS)
@ -351,8 +349,8 @@ class TestAgentSwitchAgent:
async def test_switch_agent_preserves_conversation_history(
self, base_config: VibeConfig, backend: FakeBackend
) -> None:
agent = AgentLoop(
base_config, agent_name=BuiltinAgentName.DEFAULT, backend=backend
agent = build_test_agent_loop(
config=base_config, agent_name=BuiltinAgentName.DEFAULT, backend=backend
)
user_msg = LLMMessage(role=Role.user, content="Hello")
assistant_msg = LLMMessage(role=Role.assistant, content="Hi there")
@ -369,8 +367,8 @@ class TestAgentSwitchAgent:
async def test_switch_to_same_agent_is_noop(
self, base_config: VibeConfig, backend: FakeBackend
) -> None:
agent = AgentLoop(
base_config, agent_name=BuiltinAgentName.DEFAULT, backend=backend
agent = build_test_agent_loop(
config=base_config, agent_name=BuiltinAgentName.DEFAULT, backend=backend
)
original_config = agent.config
@ -393,13 +391,11 @@ class TestAcceptEditsAgent:
async def test_accept_edits_agent_auto_approves_write_file(self) -> None:
backend = FakeBackend([])
config = VibeConfig(
session_logging=SessionLoggingConfig(enabled=False),
auto_compact_threshold=0,
enabled_tools=["write_file"],
config = build_test_vibe_config(
auto_compact_threshold=0, enabled_tools=["write_file"]
)
agent = AgentLoop(
config, agent_name=BuiltinAgentName.ACCEPT_EDITS, backend=backend
agent = build_test_agent_loop(
config=config, agent_name=BuiltinAgentName.ACCEPT_EDITS, backend=backend
)
perm = agent.tool_manager.get_tool_config("write_file").permission
@ -409,13 +405,11 @@ class TestAcceptEditsAgent:
async def test_accept_edits_agent_requires_approval_for_other_tools(self) -> None:
backend = FakeBackend([])
config = VibeConfig(
session_logging=SessionLoggingConfig(enabled=False),
auto_compact_threshold=0,
enabled_tools=["bash"],
config = build_test_vibe_config(
auto_compact_threshold=0, enabled_tools=["bash"]
)
agent = AgentLoop(
config, agent_name=BuiltinAgentName.ACCEPT_EDITS, backend=backend
agent = build_test_agent_loop(
config=config, agent_name=BuiltinAgentName.ACCEPT_EDITS, backend=backend
)
perm = agent.tool_manager.get_tool_config("bash").permission
@ -431,11 +425,10 @@ class TestPlanAgentToolRestriction:
usage=LLMUsage(prompt_tokens=10, completion_tokens=5),
)
])
config = VibeConfig(
session_logging=SessionLoggingConfig(enabled=False),
auto_compact_threshold=0,
config = build_test_vibe_config(auto_compact_threshold=0)
agent = build_test_agent_loop(
config=config, agent_name=BuiltinAgentName.PLAN, backend=backend
)
agent = AgentLoop(config, agent_name=BuiltinAgentName.PLAN, backend=backend)
tool_names = set(agent.tool_manager.available_tools.keys())
@ -457,11 +450,10 @@ class TestPlanAgentToolRestriction:
mock_llm_chunk(content="Tool not available"),
])
config = VibeConfig(
session_logging=SessionLoggingConfig(enabled=False),
auto_compact_threshold=0,
config = build_test_vibe_config(auto_compact_threshold=0)
agent = build_test_agent_loop(
config=config, agent_name=BuiltinAgentName.PLAN, backend=backend
)
agent = AgentLoop(config, agent_name=BuiltinAgentName.PLAN, backend=backend)
events = [ev async for ev in agent.act("Run ls")]
@ -476,7 +468,7 @@ class TestPlanAgentToolRestriction:
class TestAgentManagerFiltering:
def test_enabled_agents_filters_to_only_enabled(self) -> None:
config = VibeConfig(
config = build_test_vibe_config(
include_project_context=False,
include_prompt_detail=False,
enabled_agents=["default", "plan"],
@ -491,7 +483,7 @@ class TestAgentManagerFiltering:
assert "accept-edits" not in agents
def test_disabled_agents_excludes_disabled(self) -> None:
config = VibeConfig(
config = build_test_vibe_config(
include_project_context=False,
include_prompt_detail=False,
disabled_agents=["auto-approve", "accept-edits"],
@ -506,7 +498,7 @@ class TestAgentManagerFiltering:
assert "accept-edits" not in agents
def test_enabled_agents_takes_precedence_over_disabled(self) -> None:
config = VibeConfig(
config = build_test_vibe_config(
include_project_context=False,
include_prompt_detail=False,
enabled_agents=["default"],
@ -519,7 +511,7 @@ class TestAgentManagerFiltering:
assert "default" in agents
def test_glob_pattern_matching(self) -> None:
config = VibeConfig(
config = build_test_vibe_config(
include_project_context=False,
include_prompt_detail=False,
disabled_agents=["auto-*", "accept-*"],
@ -533,7 +525,7 @@ class TestAgentManagerFiltering:
assert "accept-edits" not in agents
def test_regex_pattern_matching(self) -> None:
config = VibeConfig(
config = build_test_vibe_config(
include_project_context=False,
include_prompt_detail=False,
enabled_agents=["re:^(default|plan)$"],
@ -546,7 +538,7 @@ class TestAgentManagerFiltering:
assert "plan" in agents
def test_empty_enabled_agents_returns_all(self) -> None:
config = VibeConfig(
config = build_test_vibe_config(
include_project_context=False,
include_prompt_detail=False,
enabled_agents=[],
@ -560,7 +552,7 @@ class TestAgentManagerFiltering:
assert "explore" in agents
def test_get_subagents_respects_filtering(self) -> None:
config = VibeConfig(
config = build_test_vibe_config(
include_project_context=False,
include_prompt_detail=False,
disabled_agents=["explore"],
@ -570,3 +562,57 @@ class TestAgentManagerFiltering:
subagents = manager.get_subagents()
names = [a.name for a in subagents]
assert "explore" not in names
class TestAgentLoopInitialization:
def test_agent_system_prompt_id_is_applied_on_init(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
project_prompts = tmp_path / "project" / ".vibe" / "prompts"
project_prompts.mkdir(parents=True)
global_prompts = tmp_path / "home" / ".vibe" / "prompts"
global_prompts.mkdir(parents=True)
custom_prompt_content = "CUSTOM_AGENT_PROMPT_MARKER"
(global_prompts / "custom_agent.md").write_text(custom_prompt_content)
monkeypatch.setattr(
"vibe.core.config.PROMPTS_DIR", ConfigPath(lambda: project_prompts)
)
monkeypatch.setattr(
"vibe.core.config.GLOBAL_PROMPTS_DIR", GlobalPath(lambda: global_prompts)
)
custom_agent = AgentProfile(
name="custom_test_agent",
display_name="Custom Test",
description="Test agent with custom system prompt",
safety=AgentSafety.NEUTRAL,
overrides={"system_prompt_id": "custom_agent"},
)
patched_agents = {**BUILTIN_AGENTS, "custom_test_agent": custom_agent}
monkeypatch.setattr("vibe.core.agents.models.BUILTIN_AGENTS", patched_agents)
monkeypatch.setattr("vibe.core.agents.manager.BUILTIN_AGENTS", patched_agents)
config = build_test_vibe_config(
include_project_context=False, include_prompt_detail=False
)
assert config.system_prompt_id == "cli", (
"Base config should use default 'cli' prompt"
)
agent_loop = build_test_agent_loop(
config=config, agent_name="custom_test_agent"
)
assert agent_loop.config.system_prompt_id == "custom_agent", (
"Merged config should have the agent's system_prompt_id override"
)
system_message = agent_loop.messages[0]
assert system_message.role == Role.system
assert system_message.content is not None
assert custom_prompt_content in system_message.content, (
f"System message should contain custom prompt content. "
f"Expected '{custom_prompt_content}' to be in system message."
)