2026-01-02 11:15:06 -08:00
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { z } from 'zod' ;
import { zodToJsonSchema } from 'zod-to-json-schema' ;
import * as path from 'node:path' ;
import { getFolderStructure } from '../utils/getFolderStructure.js' ;
import type { MessageBus } from '../confirmation-bus/message-bus.js' ;
import type {
ToolResult ,
ToolCallConfirmationDetails ,
ToolInvocation ,
ToolConfirmationOutcome ,
} from './tools.js' ;
import { BaseDeclarativeTool , BaseToolInvocation , Kind } from './tools.js' ;
import type { Config } from '../config/config.js' ;
import { ACTIVATE_SKILL_TOOL_NAME } from './tool-names.js' ;
/**
* Parameters for the ActivateSkill tool
*/
export interface ActivateSkillToolParams {
/**
* The name of the skill to activate
*/
name : string ;
}
class ActivateSkillToolInvocation extends BaseToolInvocation <
ActivateSkillToolParams ,
ToolResult
> {
private cachedFolderStructure : string | undefined ;
constructor (
private config : Config ,
params : ActivateSkillToolParams ,
2026-01-04 17:11:43 -05:00
messageBus : MessageBus ,
2026-01-02 11:15:06 -08:00
_toolName? : string ,
_toolDisplayName? : string ,
) {
super ( params , messageBus , _toolName , _toolDisplayName ) ;
}
getDescription ( ) : string {
const skillName = this . params . name ;
const skill = this . config . getSkillManager ( ) . getSkill ( skillName ) ;
if ( skill ) {
return ` " ${ skillName } ": ${ skill . description } ` ;
}
return ` " ${ skillName } " (⚠️ unknown skill) ` ;
}
private async getOrFetchFolderStructure (
skillLocation : string ,
) : Promise < string > {
if ( this . cachedFolderStructure === undefined ) {
this . cachedFolderStructure = await getFolderStructure (
path . dirname ( skillLocation ) ,
) ;
}
return this . cachedFolderStructure ;
}
protected override async getConfirmationDetails (
_abortSignal : AbortSignal ,
) : Promise < ToolCallConfirmationDetails | false > {
if ( ! this . messageBus ) {
return false ;
}
const skillName = this . params . name ;
const skill = this . config . getSkillManager ( ) . getSkill ( skillName ) ;
if ( ! skill ) {
return false ;
}
const folderStructure = await this . getOrFetchFolderStructure (
skill . location ,
) ;
const confirmationDetails : ToolCallConfirmationDetails = {
type : 'info' ,
title : ` Activate Skill: ${ skillName } ` ,
prompt : ` You are about to enable the specialized agent skill ** ${ skillName } **.
**Description:**
${ skill . description }
**Resources to be shared with the model:**
${ folderStructure } ` ,
onConfirm : async ( outcome : ToolConfirmationOutcome ) = > {
await this . publishPolicyUpdate ( outcome ) ;
} ,
} ;
return confirmationDetails ;
}
async execute ( _signal : AbortSignal ) : Promise < ToolResult > {
const skillName = this . params . name ;
const skillManager = this . config . getSkillManager ( ) ;
const skill = skillManager . getSkill ( skillName ) ;
if ( ! skill ) {
const skills = skillManager . getSkills ( ) ;
return {
llmContent : ` Error: Skill " ${ skillName } " not found. Available skills are: ${ skills . map ( ( s ) = > s . name ) . join ( ', ' ) } ` ,
returnDisplay : ` Skill " ${ skillName } " not found. ` ,
} ;
}
skillManager . activateSkill ( skillName ) ;
2026-01-06 11:24:37 -08:00
// Add the skill's directory to the workspace context so the agent has permission
// to read its bundled resources.
this . config
. getWorkspaceContext ( )
. addDirectory ( path . dirname ( skill . location ) ) ;
2026-01-02 11:15:06 -08:00
const folderStructure = await this . getOrFetchFolderStructure (
skill . location ,
) ;
return {
llmContent : ` <ACTIVATED_SKILL name=" ${ skillName } ">
<INSTRUCTIONS>
${ skill . body }
</INSTRUCTIONS>
<AVAILABLE_RESOURCES>
${ folderStructure }
</AVAILABLE_RESOURCES>
</ACTIVATED_SKILL> ` ,
returnDisplay : ` Skill ** ${ skillName } ** activated. Resources loaded from \` ${ path . dirname ( skill . location ) } \` : \ n \ n ${ folderStructure } ` ,
} ;
}
}
/**
* Implementation of the ActivateSkill tool logic
*/
export class ActivateSkillTool extends BaseDeclarativeTool <
ActivateSkillToolParams ,
ToolResult
> {
static readonly Name = ACTIVATE_SKILL_TOOL_NAME ;
constructor (
private config : Config ,
2026-01-04 17:11:43 -05:00
messageBus : MessageBus ,
2026-01-02 11:15:06 -08:00
) {
const skills = config . getSkillManager ( ) . getSkills ( ) ;
const skillNames = skills . map ( ( s ) = > s . name ) ;
let schema : z.ZodTypeAny ;
if ( skillNames . length === 0 ) {
schema = z . object ( {
name : z.string ( ) . describe ( 'No skills are currently available.' ) ,
} ) ;
} else {
schema = z . object ( {
name : z
. enum ( skillNames as [ string , . . . string [ ] ] )
. describe ( 'The name of the skill to activate.' ) ,
} ) ;
}
super (
ActivateSkillTool . Name ,
'Activate Skill' ,
"Activates a specialized agent skill by name. Returns the skill's instructions wrapped in `<ACTIVATED_SKILL>` tags. These provide specialized guidance for the current task. Use this when you identify a task that matches a skill's description." ,
Kind . Other ,
zodToJsonSchema ( schema ) ,
2026-01-04 17:11:43 -05:00
messageBus ,
2026-01-02 11:15:06 -08:00
true ,
false ,
) ;
}
protected createInvocation (
params : ActivateSkillToolParams ,
2026-01-04 17:11:43 -05:00
messageBus : MessageBus ,
2026-01-02 11:15:06 -08:00
_toolName? : string ,
_toolDisplayName? : string ,
) : ToolInvocation < ActivateSkillToolParams , ToolResult > {
return new ActivateSkillToolInvocation (
this . config ,
params ,
messageBus ,
_toolName ,
_toolDisplayName ? ? 'Activate Skill' ,
) ;
}
}