Merge pull request #3187 from code-yeongyu/fix/issue-2687
fix: propagate project skills to background task sessions (#2687)
This commit is contained in:
@@ -0,0 +1,125 @@
|
|||||||
|
declare const require: (name: string) => unknown
|
||||||
|
const { describe, test, expect } = require("bun:test") as {
|
||||||
|
describe: (name: string, fn: () => void) => void
|
||||||
|
test: (name: string, fn: () => void) => void
|
||||||
|
expect: (value: unknown) => {
|
||||||
|
toBe: (expected: unknown) => void
|
||||||
|
toContain: (expected: string) => void
|
||||||
|
toBeUndefined: () => void
|
||||||
|
toBeDefined: () => void
|
||||||
|
not: {
|
||||||
|
toContain: (expected: string) => void
|
||||||
|
toBeUndefined: () => void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
import { buildSystemContent } from "./prompt-builder"
|
||||||
|
import type { AvailableSkill, AvailableCategory } from "../../agents/dynamic-agent-prompt-builder"
|
||||||
|
|
||||||
|
describe("prompt-builder", () => {
|
||||||
|
describe("buildSystemContent", () => {
|
||||||
|
describe("#given non-plan agent with availableSkills", () => {
|
||||||
|
test("#when availableSkills contains project-level skills #then system content includes available_skills section", () => {
|
||||||
|
// given
|
||||||
|
const availableSkills: AvailableSkill[] = [
|
||||||
|
{ name: "git-master", description: "Git workflow automation", location: "plugin" },
|
||||||
|
{ name: "my-project-skill", description: "Project-specific deployment", location: "project" },
|
||||||
|
]
|
||||||
|
const availableCategories: AvailableCategory[] = [
|
||||||
|
{ name: "quick", description: "Trivial tasks", model: "openai/gpt-5.4-mini" },
|
||||||
|
]
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = buildSystemContent({
|
||||||
|
agentName: "sisyphus-junior",
|
||||||
|
availableSkills,
|
||||||
|
availableCategories,
|
||||||
|
})
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result).toBeDefined()
|
||||||
|
expect(result).toContain("my-project-skill")
|
||||||
|
expect(result).toContain("git-master")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("#when agent is explore #then system content includes available_skills section", () => {
|
||||||
|
// given
|
||||||
|
const availableSkills: AvailableSkill[] = [
|
||||||
|
{ name: "code-review", description: "Review code quality", location: "project" },
|
||||||
|
]
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = buildSystemContent({
|
||||||
|
agentName: "explore",
|
||||||
|
availableSkills,
|
||||||
|
})
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result).toBeDefined()
|
||||||
|
expect(result).toContain("code-review")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("#when availableSkills is empty #then system content does not include available_skills section", () => {
|
||||||
|
// given
|
||||||
|
const availableSkills: AvailableSkill[] = []
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = buildSystemContent({
|
||||||
|
agentName: "sisyphus-junior",
|
||||||
|
availableSkills,
|
||||||
|
categoryPromptAppend: "some category context",
|
||||||
|
})
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result).toBeDefined()
|
||||||
|
expect(result).not.toContain("available_skills")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#given plan agent with availableSkills", () => {
|
||||||
|
test("#when availableSkills provided #then system content includes plan agent prepend with skills", () => {
|
||||||
|
// given
|
||||||
|
const availableSkills: AvailableSkill[] = [
|
||||||
|
{ name: "git-master", description: "Git workflow automation", location: "plugin" },
|
||||||
|
]
|
||||||
|
const availableCategories: AvailableCategory[] = [
|
||||||
|
{ name: "quick", description: "Trivial tasks", model: "openai/gpt-5.4-mini" },
|
||||||
|
]
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = buildSystemContent({
|
||||||
|
agentName: "plan",
|
||||||
|
availableSkills,
|
||||||
|
availableCategories,
|
||||||
|
})
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result).toBeDefined()
|
||||||
|
expect(result).toContain("git-master")
|
||||||
|
expect(result).toContain("AVAILABLE SKILLS")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("#given non-plan agent with agentsContext override", () => {
|
||||||
|
test("#when agentsContext is provided #then it takes precedence and skills section is appended", () => {
|
||||||
|
// given
|
||||||
|
const availableSkills: AvailableSkill[] = [
|
||||||
|
{ name: "deploy-skill", description: "Deployment automation", location: "project" },
|
||||||
|
]
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = buildSystemContent({
|
||||||
|
agentName: "sisyphus-junior",
|
||||||
|
agentsContext: "Custom agent context here",
|
||||||
|
availableSkills,
|
||||||
|
})
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result).toBeDefined()
|
||||||
|
expect(result).toContain("Custom agent context here")
|
||||||
|
expect(result).toContain("deploy-skill")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { BuildSystemContentInput } from "./types"
|
import type { BuildSystemContentInput } from "./types"
|
||||||
|
import type { AvailableSkill } from "../../agents/dynamic-agent-prompt-builder"
|
||||||
import { buildPlanAgentSystemPrepend, isPlanAgent } from "./constants"
|
import { buildPlanAgentSystemPrepend, isPlanAgent } from "./constants"
|
||||||
import { buildSystemContentWithTokenLimit } from "./token-limiter"
|
import { buildSystemContentWithTokenLimit } from "./token-limiter"
|
||||||
|
|
||||||
@@ -21,6 +22,22 @@ ${TDD_LINE}`
|
|||||||
return PLAN_AGENT_PROMPT_BASE
|
return PLAN_AGENT_PROMPT_BASE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildAvailableSkillsSection(skills: AvailableSkill[]): string {
|
||||||
|
if (skills.length === 0) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows = skills
|
||||||
|
.map((s) => `- \`${s.name}\`: ${s.description || s.name}`)
|
||||||
|
.join("\n")
|
||||||
|
|
||||||
|
return `<available_skills>
|
||||||
|
Skills provide specialized instructions. Load via load_skills parameter when delegating tasks.
|
||||||
|
|
||||||
|
${rows}
|
||||||
|
</available_skills>`
|
||||||
|
}
|
||||||
|
|
||||||
function usesFreeOrLocalModel(model: { providerID: string; modelID: string; variant?: string } | undefined): boolean {
|
function usesFreeOrLocalModel(model: { providerID: string; modelID: string; variant?: string } | undefined): boolean {
|
||||||
if (!model) {
|
if (!model) {
|
||||||
return false
|
return false
|
||||||
@@ -51,10 +68,20 @@ export function buildSystemContent(input: BuildSystemContentInput): string | und
|
|||||||
availableSkills,
|
availableSkills,
|
||||||
} = input
|
} = input
|
||||||
|
|
||||||
const planAgentPrepend = isPlanAgent(agentName)
|
const isPlan = isPlanAgent(agentName)
|
||||||
|
const planAgentPrepend = isPlan
|
||||||
? buildPlanAgentSystemPrepend(availableCategories, availableSkills)
|
? buildPlanAgentSystemPrepend(availableCategories, availableSkills)
|
||||||
: ""
|
: ""
|
||||||
|
|
||||||
|
const skillsSection = !isPlan
|
||||||
|
? buildAvailableSkillsSection(availableSkills ?? [])
|
||||||
|
: ""
|
||||||
|
|
||||||
|
const baseAgentsContext = agentsContext ?? planAgentPrepend
|
||||||
|
const effectiveAgentsContext = !isPlan && skillsSection
|
||||||
|
? [baseAgentsContext, skillsSection].filter(Boolean).join("\n\n")
|
||||||
|
: baseAgentsContext
|
||||||
|
|
||||||
const effectiveMaxPromptTokens = maxPromptTokens
|
const effectiveMaxPromptTokens = maxPromptTokens
|
||||||
?? (usesFreeOrLocalModel(model) ? FREE_OR_LOCAL_PROMPT_TOKEN_LIMIT : undefined)
|
?? (usesFreeOrLocalModel(model) ? FREE_OR_LOCAL_PROMPT_TOKEN_LIMIT : undefined)
|
||||||
|
|
||||||
@@ -63,7 +90,7 @@ export function buildSystemContent(input: BuildSystemContentInput): string | und
|
|||||||
skillContent,
|
skillContent,
|
||||||
skillContents,
|
skillContents,
|
||||||
categoryPromptAppend,
|
categoryPromptAppend,
|
||||||
agentsContext: agentsContext ?? planAgentPrepend,
|
agentsContext: effectiveAgentsContext,
|
||||||
planAgentPrepend,
|
planAgentPrepend,
|
||||||
},
|
},
|
||||||
effectiveMaxPromptTokens
|
effectiveMaxPromptTokens
|
||||||
|
|||||||
Reference in New Issue
Block a user