(tool_input: dict[str, Any], context: ToolContext)
| 152 | return filter_agents_by_mcp_requirements(agents, available_mcp) |
| 153 | |
| 154 | def _agent_call(tool_input: dict[str, Any], context: ToolContext) -> ToolResult: |
| 155 | prompt = tool_input.get("prompt", "") |
| 156 | if not prompt: |
| 157 | raise ToolInputError("prompt is required") |
| 158 | |
| 159 | description = tool_input.get("description", prompt[:50]) |
| 160 | subagent_type = tool_input.get("subagent_type") |
| 161 | model = tool_input.get("model") |
| 162 | run_in_background = bool(tool_input.get("run_in_background", False)) |
| 163 | # Chapter-10 / WI-6.1 — optional human-readable name. We |
| 164 | # validate / register it AFTER agent_id is generated so the |
| 165 | # collision-on-running check can compare against the registry |
| 166 | # state under the registry's own atomicity guarantees. |
| 167 | agent_name = tool_input.get("name") |
| 168 | if agent_name is not None and not isinstance(agent_name, str): |
| 169 | raise ToolInputError("name must be a string when provided") |
| 170 | if isinstance(agent_name, str) and not agent_name.strip(): |
| 171 | agent_name = None # treat empty/whitespace as absent |
| 172 | |
| 173 | # Resolve agent definition. |
| 174 | # |
| 175 | # Routing rules mirror typescript/src/tools/AgentTool/AgentTool.tsx:318-356: |
| 176 | # - subagent_type provided → use it (explicit wins). |
| 177 | # - subagent_type omitted, fork gate on → implicit fork via FORK_AGENT. |
| 178 | # - subagent_type omitted, fork gate off → default to general-purpose. |
| 179 | agent_definitions = _get_agent_definitions(context) |
| 180 | is_fork_path = ( |
| 181 | subagent_type is None and is_fork_subagent_enabled(context) |
| 182 | ) |
| 183 | |
| 184 | if is_fork_path: |
| 185 | # Recursive-fork guard. Primary check: querySource on the parent's |
| 186 | # options. Secondary check: scan parent messages for the fork |
| 187 | # boilerplate tag. Either one trips means we are already inside a |
| 188 | # fork child, so refuse to spawn another. |
| 189 | parent_query_source = getattr(context.options, "query_source", None) |
| 190 | if parent_query_source == f"agent:builtin:{FORK_SUBAGENT_TYPE}" or is_in_fork_child(context.messages): |
| 191 | raise ToolInputError( |
| 192 | "Fork is not available inside a forked worker. " |
| 193 | "Complete your task directly using your tools." |
| 194 | ) |
| 195 | agent_def = FORK_AGENT |
| 196 | elif subagent_type: |
| 197 | agent_def = find_agent_by_type(agent_definitions, subagent_type) |
| 198 | if agent_def is None: |
| 199 | available = [a.agent_type for a in agent_definitions] |
| 200 | raise ToolInputError( |
| 201 | f"Unknown subagent_type: {subagent_type}. " |
| 202 | f"Available: {', '.join(available)}" |
| 203 | ) |
| 204 | else: |
| 205 | # Default to general-purpose |
| 206 | agent_def = ( |
| 207 | find_agent_by_type(agent_definitions, "general-purpose") |
| 208 | or agent_definitions[0] |
| 209 | if agent_definitions |
| 210 | else None |
| 211 | ) |
nothing calls this directly
no test coverage detected