feat(subagents): support per-subagent skill loading and custom subagent types (#2253)

* feat(subagents): support per-subagent skill loading and custom subagent types (#2230)

Add per-subagent skill configuration and custom subagent type registration,
aligned with Codex's role-based config layering and per-session skill injection.

Backend:
- SubagentConfig gains `skills` field (None=all, []=none, list=whitelist)
- New CustomSubagentConfig for user-defined subagent types in config.yaml
- SubagentsAppConfig gains `custom_agents` section and `get_skills_for()`
- Registry resolves custom agents with three-layer config precedence
- SubagentExecutor loads skills per-session as conversation items (Codex pattern)
- task_tool no longer appends skills to system_prompt
- Lead agent system prompt dynamically lists all registered subagent types
- setup_agent tool accepts optional skills parameter
- Gateway agents API transparently passes skills in CRUD operations

Frontend:
- Agent/CreateAgentRequest/UpdateAgentRequest types include skills field
- Agent card displays skills as badges alongside tool_groups

Config:
- config.example.yaml documents custom_agents and per-agent skills override

Tests:
- 40 new tests covering all skill config, custom agents, and registry logic
- Existing tests updated for new get_skills_prompt_section signature

Closes #2230

* fix: address review feedback on skills PR

- Remove stale get_skills_prompt_section monkeypatches from test_task_tool_core_logic.py
  (task_tool no longer imports this function after skill injection moved to executor)
- Add key prefixes (tg:/sk:) to agent-card badges to prevent React key collisions
  between tool_groups and skills

* fix(ci): resolve lint and test failures

- Format agent-card.tsx with prettier (lint-frontend)
- Remove stale "Skills Appendix" system_prompt assertion — skills are now
  loaded per-session by SubagentExecutor, not appended to system_prompt

* fix(ci): sort imports in test_subagent_skills_config.py (ruff I001)

* fix(ci): use nullish coalescing in agent-card badge condition (eslint)

* fix: address review feedback on skills PR

- Use model_fields_set in AgentUpdateRequest to distinguish "field omitted"
  from "explicitly set to null" — fixes skills=None ambiguity where None
  means "inherit all" but was treated as "don't change"
- Move lazy import of get_subagent_config outside loop in
  _build_available_subagents_description to avoid repeated import overhead

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
Xinmin Zeng
2026-04-23 23:59:47 +08:00
committed by GitHub
parent 4e72410154
commit 30d619de08
14 changed files with 962 additions and 72 deletions
+27 -1
View File
@@ -577,15 +577,41 @@ sandbox:
# # Optional global max-turn override for all subagents
# # max_turns: 120
#
# # Optional per-agent overrides
# # Optional per-agent overrides (applies to both built-in and custom agents)
# agents:
# general-purpose:
# timeout_seconds: 1800 # 30 minutes for complex multi-step tasks
# max_turns: 160
# # model: qwen3:32b # Use a specific model (default: inherit from lead agent)
# # skills: # Skill whitelist (default: inherit all enabled skills)
# # - web-search
# # - data-analysis
# bash:
# timeout_seconds: 300 # 5 minutes for quick command execution
# max_turns: 80
# # skills: [] # No skills for bash agent
#
# # Custom subagent types: define specialized agents with their own prompts,
# # tools, skills, and model configuration. Custom agents are available via
# # the `task` tool alongside built-in types (general-purpose, bash).
# # custom_agents:
# # analysis:
# # description: "Data analysis specialist for processing datasets and generating insights"
# # system_prompt: |
# # You are a data analysis subagent. Focus on:
# # - Processing and analyzing datasets
# # - Generating visualizations
# # - Providing statistical insights
# # tools: # Tool whitelist (null = inherit all)
# # - bash
# # - read_file
# # - write_file
# # skills: # Skill whitelist (null = inherit all, [] = none)
# # - data-analysis
# # - visualization
# # model: inherit # 'inherit' uses parent's model
# # max_turns: 80
# # timeout_seconds: 600
#
# # Model override: by default, subagents inherit the lead agent's model.
# # Set `model` to use a different model (e.g., a local Ollama model for cost savings).