From af8026e02315ff819193b0fb06a07a220efa9dd7 Mon Sep 17 00:00:00 2001 From: Edison-A-N Date: Mon, 12 Jan 2026 15:28:36 +0800 Subject: [PATCH 1/2] feat(agent-manager): add auto_start_agents configuration to automatically start discovered agents --- src/openagents/core/agent_manager.py | 62 +++++++++++- src/openagents/core/network.py | 6 +- src/openagents/models/network_config.py | 6 ++ tests/agents/test_agent_manager.py | 122 ++++++++++++++++++++++++ 4 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 tests/agents/test_agent_manager.py diff --git a/src/openagents/core/agent_manager.py b/src/openagents/core/agent_manager.py index 08c70166..420227d2 100644 --- a/src/openagents/core/agent_manager.py +++ b/src/openagents/core/agent_manager.py @@ -48,16 +48,18 @@ class AgentManager: and log file management for all agents in workspace/agents/ directory. """ - def __init__(self, workspace_path: Path): + def __init__(self, workspace_path: Path, auto_start_agents: bool = False): """Initialize agent manager. Args: workspace_path: Path to workspace directory + auto_start_agents: If True, automatically start all discovered agents during initialization """ self.workspace_path = Path(workspace_path) self.agents_dir = self.workspace_path / "agents" self.logs_dir = self.workspace_path / "logs" / "agents" self.env_vars_dir = self.workspace_path / "config" / "agent_env" + self.auto_start_agents = auto_start_agents # Ensure directories exist self.logs_dir.mkdir(parents=True, exist_ok=True) @@ -73,7 +75,7 @@ def __init__(self, workspace_path: Path): # Reference to network for agent unregistration on stop self._network = None - logger.info(f"AgentManager initialized for workspace: {self.workspace_path}") + logger.info(f"AgentManager initialized for workspace: {self.workspace_path} (auto_start_agents={auto_start_agents})") def set_network(self, network) -> None: """Set the network reference for agent unregistration. @@ -102,6 +104,11 @@ async def start(self) -> bool: self.is_running = True logger.info(f"AgentManager started with {len(self.agents)} discovered agents") + + # Auto-start agents if configured + if self.auto_start_agents: + await self._auto_start_all_agents() + return True except Exception as e: @@ -950,7 +957,56 @@ async def _monitor_processes(self) -> None: except Exception as e: logger.error(f"Error in process monitor: {e}") await asyncio.sleep(2.0) - + + async def _auto_start_all_agents(self) -> None: + """Automatically start all discovered agents when auto_start_agents is enabled. + + This method is called during AgentManager initialization if auto_start_agents is True. + It starts all agents that have been discovered. + """ + all_agents_status = self.get_all_agents_status() + + if not all_agents_status: + logger.info("No agents discovered, skipping auto-start") + return + + # Filter out agents that are already running + agents_to_start = [ + agent_status + for agent_status in all_agents_status + if agent_status and agent_status.get("status") != "running" + ] + + if not agents_to_start: + logger.info("All discovered agents are already running") + return + + logger.info(f"Auto-starting {len(agents_to_start)} agent(s)...") + + # Start agents with a small delay between each to avoid overwhelming the system + for agent_status in agents_to_start: + agent_id = agent_status.get("agent_id") + if not agent_id: + continue + + try: + logger.info(f"Auto-starting agent: {agent_id}") + result = await self.start_agent(agent_id) + if result.get("success"): + logger.info(f"✅ Successfully auto-started agent: {agent_id}") + else: + error_msg = result.get("message", "Unknown error") + logger.warning( + f"⚠️ Failed to auto-start agent '{agent_id}': {error_msg}" + ) + + # Small delay between starts to avoid overwhelming the system + await asyncio.sleep(0.5) + except Exception as e: + logger.error(f"Error auto-starting agent '{agent_id}': {e}") + + logger.info(f"Auto-start completed for {len(agents_to_start)} agent(s)") + async def _stop_all_agents(self) -> None: """Stop all running agents.""" running_agents = [ diff --git a/src/openagents/core/network.py b/src/openagents/core/network.py index 1373b031..8fe5e0d7 100644 --- a/src/openagents/core/network.py +++ b/src/openagents/core/network.py @@ -89,7 +89,11 @@ def __init__(self, config: NetworkConfig, workspace_path: Optional[str]): if self.workspace_manager: from openagents.core.agent_manager import AgentManager - self.agent_manager = AgentManager(self.workspace_manager.workspace_path) + # Pass auto_start_agents config to AgentManager + self.agent_manager = AgentManager( + self.workspace_manager.workspace_path, + auto_start_agents=self.config.auto_start_agents + ) # Set network reference for agent unregistration on stop self.agent_manager.set_network(self) diff --git a/src/openagents/models/network_config.py b/src/openagents/models/network_config.py index 568223fd..b4648aba 100644 --- a/src/openagents/models/network_config.py +++ b/src/openagents/models/network_config.py @@ -162,6 +162,12 @@ class NetworkConfig(BaseModel): "When False, agents without password_hash are assigned to default_agent_group.", ) + # Agent lifecycle + auto_start_agents: bool = Field( + default=False, + description="When True, automatically start all discovered agents during network initialization.", + ) + # Network initialization state initialized: bool = Field( default=False, diff --git a/tests/agents/test_agent_manager.py b/tests/agents/test_agent_manager.py new file mode 100644 index 00000000..2078361b --- /dev/null +++ b/tests/agents/test_agent_manager.py @@ -0,0 +1,122 @@ +""" +Tests for AgentManager auto-start functionality. +""" + +import pytest +from unittest.mock import AsyncMock, MagicMock + +from openagents.core.agent_manager import AgentManager + + +@pytest.fixture +def tmp_workspace(tmp_path): + """Create a temporary workspace directory.""" + workspace = tmp_path / "workspace" + workspace.mkdir() + (workspace / "agents").mkdir() + (workspace / "logs").mkdir() + (workspace / "config").mkdir() + return workspace + + +@pytest.mark.asyncio +async def test_auto_start_enabled(tmp_workspace): + """Test that agents are auto-started when auto_start_agents=True.""" + manager = AgentManager(tmp_workspace, auto_start_agents=True) + + # Mock the methods + manager.get_all_agents_status = MagicMock(return_value=[ + {"agent_id": "agent1", "status": "stopped"}, + {"agent_id": "agent2", "status": "stopped"}, + ]) + manager.start_agent = AsyncMock(return_value={"success": True}) + + # Start the manager + await manager.start() + + # Verify start_agent was called for each agent + assert manager.start_agent.call_count == 2 + manager.start_agent.assert_any_call("agent1") + manager.start_agent.assert_any_call("agent2") + + +@pytest.mark.asyncio +async def test_auto_start_disabled(tmp_workspace): + """Test that agents are not auto-started when auto_start_agents=False.""" + manager = AgentManager(tmp_workspace, auto_start_agents=False) + + # Mock the methods + manager.get_all_agents_status = MagicMock(return_value=[ + {"agent_id": "agent1", "status": "stopped"}, + ]) + manager.start_agent = AsyncMock(return_value={"success": True}) + + # Start the manager + await manager.start() + + # Verify start_agent was never called + manager.start_agent.assert_not_called() + + +@pytest.mark.asyncio +async def test_auto_start_filters_running_agents(tmp_workspace): + """Test that running agents are not restarted.""" + manager = AgentManager(tmp_workspace, auto_start_agents=True) + + # Mock agents with mixed status + manager.get_all_agents_status = MagicMock(return_value=[ + {"agent_id": "agent1", "status": "running"}, + {"agent_id": "agent2", "status": "stopped"}, + {"agent_id": "agent3", "status": "running"}, + ]) + manager.start_agent = AsyncMock(return_value={"success": True}) + + # Start the manager + await manager.start() + + # Only agent2 should be started + assert manager.start_agent.call_count == 1 + manager.start_agent.assert_called_with("agent2") + + +@pytest.mark.asyncio +async def test_auto_start_with_empty_agents(tmp_workspace): + """Test auto-start handles empty agent list gracefully.""" + manager = AgentManager(tmp_workspace, auto_start_agents=True) + + # Mock empty agent list + manager.get_all_agents_status = MagicMock(return_value=[]) + manager.start_agent = AsyncMock(return_value={"success": True}) + + # Start the manager - should not raise any errors + await manager.start() + + # No agents to start + manager.start_agent.assert_not_called() + + +@pytest.mark.asyncio +async def test_auto_start_continues_on_failure(tmp_workspace): + """Test that auto-start continues even if one agent fails.""" + manager = AgentManager(tmp_workspace, auto_start_agents=True) + + # Mock agents + manager.get_all_agents_status = MagicMock(return_value=[ + {"agent_id": "agent1", "status": "stopped"}, + {"agent_id": "agent2", "status": "stopped"}, + {"agent_id": "agent3", "status": "stopped"}, + ]) + + # Make agent2 fail + async def mock_start_agent(agent_id): + if agent_id == "agent2": + return {"success": False, "message": "Start failed"} + return {"success": True} + + manager.start_agent = AsyncMock(side_effect=mock_start_agent) + + # Start the manager + await manager.start() + + # All agents should be attempted + assert manager.start_agent.call_count == 3 From 26b24993221b7c2743367369c03cb26ff868a2fa Mon Sep 17 00:00:00 2001 From: Edison-A-N Date: Tue, 13 Jan 2026 11:39:28 +0800 Subject: [PATCH 2/2] docs(network-config): add auto_start_agents configuration documentation --- docs/network-configuration/network-configuration.mdx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/network-configuration/network-configuration.mdx b/docs/network-configuration/network-configuration.mdx index f5722030..8f3b38a9 100644 --- a/docs/network-configuration/network-configuration.mdx +++ b/docs/network-configuration/network-configuration.mdx @@ -72,8 +72,14 @@ network: message_queue_size: 1000 # Internal message queue size message_timeout: 30.0 # Message processing timeout message_routing_enabled: true # Enable intelligent message routing + + # Agent lifecycle + auto_start_agents: false # Automatically start all discovered agents during network initialization ``` +**Agent Lifecycle:** +- `auto_start_agents` - When `true`, automatically starts all agents discovered in `workspace/agents/` directory during network initialization. Useful for development and debugging workflows to avoid manual agent startup via admin interface. + ## Transport Configuration Networks support multiple transport protocols for agent communication. @@ -445,6 +451,7 @@ network: # Development-friendly settings encryption_enabled: false disable_agent_secret_verification: true + auto_start_agents: true # Auto-start agents for easier debugging mods: - name: "openagents.mods.workspace.messaging"