"""Built-in tool for switching the active agent profile mid-session."""
from navi.tools._internal.base import Tool, ToolContext, ToolResult, current_event_sink, current_session_id
class SwitchProfileTool(Tool):
name = "switch_profile"
description = (
"Switch this session to a different agent profile. "
"Call this on your own judgment when the task fits another profile's specialization — "
"do not ask the user for permission first. "
"The new profile (tools + system prompt) becomes active from the NEXT user message. "
"After switching, briefly inform the user: which profile is now active and why you switched."
)
parameters = {
"type": "object",
"properties": {
"profile_id": {
"type": "string",
"description": "ID of the profile to switch to.",
}
},
"required": ["profile_id"],
}
def __init__(self, session_store, profile_registry) -> None:
self._sessions = session_store
self._profiles = profile_registry
async def execute(self, params: dict, ctx: ToolContext | None = None) -> ToolResult:
profile_id = (params.get("profile_id") or "").strip()
available = ", ".join(p.id for p in self._profiles.all())
try:
profile = self._profiles.get(profile_id)
except Exception:
return ToolResult(
success=False,
output="",
error=f"Profile '{profile_id}' not found. Available: {available}",
)
if getattr(profile, "is_subagent_only", False):
return ToolResult(
success=False,
output=f"Profile '{profile_id}' is a sub-agent specialist and cannot be switched to directly. "
f"Use spawn_agent(profile_id='{profile_id}') to delegate tasks to it.",
error="subagent_only",
)
sid = ctx.session_id if ctx else current_session_id.get()
if not sid:
return ToolResult(success=False, output="", error="No active session context.")
session = await self._sessions.get(sid)
if session is None:
return ToolResult(success=False, output="", error="Session not found.")
if session.profile_id == profile_id:
return ToolResult(
success=True,
output=f"Already on profile '{profile.name}' — no change.",
)
session.profile_id = profile_id
await self._sessions.save(session)
# Notify the client immediately so it can update the UI.
sink = ctx.event_sink if ctx else current_event_sink.get()
if sink is not None:
from navi.core.events import ProfileSwitched
await sink.put(ProfileSwitched(profile_id=profile_id, profile_name=profile.name))
return ToolResult(
success=True,
output=(
f"Switched to profile '{profile.name}' ({profile_id}). "
f"Its tools and system prompt are active from the next message."
),
)