088693697a
Skills with an `agent` frontmatter field are intended for a specific agent. Previously they still appeared in: - every agent's system prompt (via `buildAvailableSkills`) - the `skill` tool's `<available_items>` description visible to all agents This wasted tokens and could mislead agents into attempting calls that would be rejected at execution time. Changes: - `buildAvailableSkills`: new optional `agentName` parameter; when provided, skills whose `definition.agent` does not match are excluded - `builtin-agents.ts`: pass per-agent name to `buildAvailableSkills` for sisyphus, hephaestus, and atlas, so each agent's prompt only lists the skills it is allowed to use - `createSkillTool` (`tools.ts`): exclude agent-restricted skills from both the eager and lazy description builds, keeping the shared tool description free of skills the current agent cannot access Execution-time enforcement (throwing on mismatch) is unchanged; this change adds the earlier, description-level visibility gate. Tests: new `available-skills.test.ts` (5 cases) + 3 new cases in `tools.test.ts` covering the description-filter and execute paths. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
44 lines
1.7 KiB
TypeScript
44 lines
1.7 KiB
TypeScript
import type { AvailableSkill } from "../dynamic-agent-prompt-builder"
|
|
import type { BrowserAutomationProvider } from "../../config/schema"
|
|
import type { LoadedSkill, SkillScope } from "../../features/opencode-skill-loader/types"
|
|
import { createBuiltinSkills } from "../../features/builtin-skills"
|
|
|
|
function mapScopeToLocation(scope: SkillScope): AvailableSkill["location"] {
|
|
if (scope === "user" || scope === "opencode") return "user"
|
|
if (scope === "project" || scope === "opencode-project") return "project"
|
|
return "plugin"
|
|
}
|
|
|
|
export function buildAvailableSkills(
|
|
discoveredSkills: LoadedSkill[],
|
|
browserProvider?: BrowserAutomationProvider,
|
|
disabledSkills?: Set<string>,
|
|
teamModeEnabled?: boolean,
|
|
agentName?: string,
|
|
): AvailableSkill[] {
|
|
const builtinSkills = createBuiltinSkills({ browserProvider, disabledSkills, teamModeEnabled })
|
|
const builtinSkillNames = new Set(builtinSkills.map(s => s.name))
|
|
|
|
const builtinAvailable: AvailableSkill[] = builtinSkills.map((skill) => ({
|
|
name: skill.name,
|
|
description: skill.description,
|
|
location: "plugin" as const,
|
|
}))
|
|
|
|
const discoveredAvailable: AvailableSkill[] = discoveredSkills
|
|
.filter(s => {
|
|
if (builtinSkillNames.has(s.name) || disabledSkills?.has(s.name)) return false
|
|
// If the skill declares an agent restriction and we know the current agent,
|
|
// exclude skills that don't belong to this agent.
|
|
if (agentName && s.definition.agent && s.definition.agent !== agentName) return false
|
|
return true
|
|
})
|
|
.map((skill) => ({
|
|
name: skill.name,
|
|
description: skill.definition.description ?? "",
|
|
location: mapScopeToLocation(skill.scope),
|
|
}))
|
|
|
|
return [...builtinAvailable, ...discoveredAvailable]
|
|
}
|