diff --git a/packages/core/src/subagents/subagent-manager.ts b/packages/core/src/subagents/subagent-manager.ts index fea33040c7..4bd5ca839e 100644 --- a/packages/core/src/subagents/subagent-manager.ts +++ b/packages/core/src/subagents/subagent-manager.ts @@ -649,6 +649,15 @@ export class SubagentManager { tools: toolNames, }; } + if (config.skills && config.tools.length > 0){ + if(toolConfig){ + toolConfig.skills = config.skills + }else{ + toolConfig = { + skills: config.skills + } + } + } return { promptConfig, diff --git a/packages/core/src/subagents/subagent.ts b/packages/core/src/subagents/subagent.ts index 4f550a36b3..aaf6b1e10e 100644 --- a/packages/core/src/subagents/subagent.ts +++ b/packages/core/src/subagents/subagent.ts @@ -19,6 +19,7 @@ import type { ToolConfirmationOutcome, ToolCallConfirmationDetails, } from '../tools/tools.js'; +import {type SkillTool} from '../tools/skill.js' import { getInitialChatHistory } from '../utils/environmentContext.js'; import type { Content, @@ -287,8 +288,10 @@ export class SubAgentScope { // If no explicit toolConfig or it contains "*" or is empty, inherit all tools. const toolsList: FunctionDeclaration[] = []; if (this.toolConfig) { + let visible_skills = this.toolConfig.skills?.length>0; + const asStrings = this.toolConfig.tools.filter( - (t): t is string => typeof t === 'string', + (t): t is string => typeof t === 'string' && (!visible_skills || t !== SkillTool.Name), ); const hasWildcard = asStrings.includes('*'); const onlyInlineDecls = this.toolConfig.tools.filter( @@ -305,6 +308,10 @@ export class SubAgentScope { toolsList.push( ...toolRegistry.getFunctionDeclarationsFiltered(asStrings), ); + + if (visible_skills){ + toolsList.push(SkillTool(this.runtimeContext, visible_skills).schema) + } } toolsList.push(...onlyInlineDecls); } else { diff --git a/packages/core/src/subagents/types.ts b/packages/core/src/subagents/types.ts index efa73a7e4d..ff521da5f6 100644 --- a/packages/core/src/subagents/types.ts +++ b/packages/core/src/subagents/types.ts @@ -39,6 +39,12 @@ export interface SubagentConfig { */ tools?: string[]; + /** + * Optional list of skills names that this subagent is allowed to use. + * If omitted, the subagent inherits all avaiable skills. + */ + skills?: string[]; + /** * System prompt content that defines the subagent's behavior. * Supports ${variable} templating via ContextState. @@ -230,6 +236,10 @@ export interface ToolConfig { * that the subagent is permitted to use. */ tools: Array; + /** + * If skills is specified seperately, ignore skills in tools, but take `skills` field to be visible for the subagent. + */ + skills: Array; } /** diff --git a/packages/core/src/tools/skill.ts b/packages/core/src/tools/skill.ts index 68ec7dd555..7f1d28a213 100644 --- a/packages/core/src/tools/skill.ts +++ b/packages/core/src/tools/skill.ts @@ -31,7 +31,7 @@ export class SkillTool extends BaseDeclarativeTool { private skillManager: SkillManager; private availableSkills: SkillConfig[] = []; - constructor(private readonly config: Config) { + constructor(private readonly config: Config, private readonly visible_skills?: Array) { // Initialize with a basic schema first const initialSchema = { type: 'object', @@ -100,6 +100,7 @@ export class SkillTool extends BaseDeclarativeTool { 'No skills are currently configured. Skills can be created by adding directories with SKILL.md files to .qwen/skills/ or ~/.qwen/skills/.'; } else { skillDescriptions = this.availableSkills + .filter((skill) => (self.visible_skills?.length>0) ? self.visible_skills.includes(skill.name) : true) .map( (skill) => `