Skip to content
3 changes: 1 addition & 2 deletions examples/01_standalone_sdk/42_file_based_subagents.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
Agent,
Conversation,
Tool,
agent_definition_to_factory,
register_agent,
)
from openhands.sdk.subagent import AgentDefinition
Expand All @@ -32,7 +31,7 @@
# 2. Register it in the delegate registry
register_agent(
name=grammar_checker.name,
factory_func=agent_definition_to_factory(grammar_checker),
factory_func=grammar_checker.to_factory(),
description=grammar_checker.description,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
)
from openhands.sdk.event import MessageEvent
from openhands.sdk.event.conversation_state import ConversationStateUpdateEvent
from openhands.sdk.subagent.registry import (
register_agent_if_absent,
)
from openhands.sdk.utils.cipher import Cipher


Expand Down Expand Up @@ -240,6 +243,20 @@ async def start_conversation(
f"{list(request.tool_module_qualnames.keys())}"
)

# Register subagent definitions from the client's registry
if request.subagent_definitions:
for agent_def in request.subagent_definitions:
factory = agent_def.to_factory()
if not agent_def.description:
agent_def = agent_def.model_copy(
update={"description": f"Remote agent: {agent_def.name}"}
)
register_agent_if_absent(factory, agent_def)
logger.info(
f"Registered {len(request.subagent_definitions)} subagent "
f"definitions for conversation {conversation_id}: "
f"{[d.name for d in request.subagent_definitions]}"
)
# Plugin loading is now handled lazily by LocalConversation.
# Just pass the plugin specs through to StoredConversation.
# LocalConversation will:
Expand Down
8 changes: 8 additions & 0 deletions openhands-agent-server/openhands/agent_server/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
ConfirmationPolicyBase,
NeverConfirm,
)
from openhands.sdk.subagent.schema import AgentDefinition
from openhands.sdk.utils.models import DiscriminatedUnionMixin, OpenHandsModel
from openhands.sdk.workspace import LocalWorkspace

Expand Down Expand Up @@ -108,6 +109,13 @@ class StartConversationRequest(BaseModel):
"to register the tools for this conversation."
),
)
subagent_definitions: list[AgentDefinition] = Field(
default_factory=list,
description=(
"Subagent definitions from the client's registry. These are "
"registered on the server so DelegateTool can advertise them."
),
)
plugins: list[PluginSource] | None = Field(
default=None,
description=(
Expand Down
2 changes: 0 additions & 2 deletions openhands-sdk/openhands/sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
)
from openhands.sdk.plugin import Plugin
from openhands.sdk.subagent import (
agent_definition_to_factory,
load_agents_from_dir,
load_project_agents,
load_user_agents,
Expand Down Expand Up @@ -124,7 +123,6 @@
"load_project_agents",
"load_user_agents",
"load_agents_from_dir",
"agent_definition_to_factory",
"load_project_skills",
"load_skills_from_dir",
"load_user_skills",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,10 +373,7 @@ def _ensure_plugins_loaded(self) -> None:

# Register file-based agents defined in plugins
if all_plugin_agents:
register_plugin_agents(
agents=all_plugin_agents,
work_dir=self.workspace.working_dir,
)
register_plugin_agents(agents=all_plugin_agents)

# Combine explicit hook_config with plugin hooks
# Explicit hooks run first (before plugin hooks)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -635,10 +635,14 @@ def __init__(

if should_create:
# Import here to avoid circular imports
from openhands.sdk.subagent.registry import (
get_registered_agent_definitions,
)
from openhands.sdk.tool.registry import get_tool_module_qualnames

tool_qualnames = get_tool_module_qualnames()
logger.debug(f"Sending tool_module_qualnames to server: {tool_qualnames}")
subagent_defs = get_registered_agent_definitions()
payload = {
"agent": agent.model_dump(
mode="json", context={"expose_secrets": True}
Expand All @@ -652,6 +656,8 @@ def __init__(
).model_dump(),
# Include tool module qualnames for dynamic registration on server
"tool_module_qualnames": tool_qualnames,
# Include subagent definitions for delegate tool on server
"subagent_definitions": [d.model_dump() for d in subagent_defs],
# Include plugins to load on server
"plugins": [p.model_dump() for p in plugins] if plugins else None,
}
Expand Down
2 changes: 0 additions & 2 deletions openhands-sdk/openhands/sdk/subagent/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
load_user_agents,
)
from openhands.sdk.subagent.registry import (
agent_definition_to_factory,
get_agent_factory,
get_factory_info,
register_agent,
Expand All @@ -29,5 +28,4 @@
"get_agent_factory",
# Agent def and factory
"AgentDefinition",
"agent_definition_to_factory",
]
Loading
Loading