feat(agents): wire agent_definitions and opencode.json agents into precedence chain
- Modified agent-config-handler.ts to load and integrate both new agent sources - Added loadAgentDefinitions() and readOpencodeConfigAgents() calls in loading phase - Integrated both sources into agent precedence chains (both Sisyphus-enabled and disabled paths) - Added detailed logging for new agent sources - Added filtering logic to respect disabled_agents configuration - Extended agent-config-handler.test.ts with 7 new integration tests - All tests passing (18/18 integration, 65/65 loader suite) Wave 3 of agent definitions enhancement complete.
This commit is contained in:
committed by
YeonGyu-Kim
parent
5755a90c3b
commit
39bda91bc7
@@ -61,6 +61,8 @@ describe("applyAgentConfig builtin override protection", () => {
|
||||
let discoverGlobalAgentsSkillsSpy: ReturnType<typeof spyOn>
|
||||
let loadUserAgentsSpy: ReturnType<typeof spyOn>
|
||||
let loadProjectAgentsSpy: ReturnType<typeof spyOn>
|
||||
let loadAgentDefinitionsSpy: ReturnType<typeof spyOn>
|
||||
let readOpencodeConfigAgentsSpy: ReturnType<typeof spyOn>
|
||||
let migrateAgentConfigSpy: ReturnType<typeof spyOn>
|
||||
let logSpy: ReturnType<typeof spyOn>
|
||||
|
||||
@@ -140,6 +142,11 @@ describe("applyAgentConfig builtin override protection", () => {
|
||||
|
||||
loadUserAgentsSpy = spyOn(agentLoader, "loadUserAgents").mockReturnValue({})
|
||||
loadProjectAgentsSpy = spyOn(agentLoader, "loadProjectAgents").mockReturnValue({})
|
||||
loadAgentDefinitionsSpy = spyOn(agentLoader, "loadAgentDefinitions").mockReturnValue({})
|
||||
readOpencodeConfigAgentsSpy = spyOn(
|
||||
agentLoader,
|
||||
"readOpencodeConfigAgents",
|
||||
).mockReturnValue({})
|
||||
|
||||
migrateAgentConfigSpy = spyOn(shared, "migrateAgentConfig").mockImplementation(
|
||||
(config: Record<string, unknown>) => config,
|
||||
@@ -159,6 +166,8 @@ describe("applyAgentConfig builtin override protection", () => {
|
||||
discoverGlobalAgentsSkillsSpy.mockRestore()
|
||||
loadUserAgentsSpy.mockRestore()
|
||||
loadProjectAgentsSpy.mockRestore()
|
||||
loadAgentDefinitionsSpy.mockRestore()
|
||||
readOpencodeConfigAgentsSpy.mockRestore()
|
||||
migrateAgentConfigSpy.mockRestore()
|
||||
logSpy.mockRestore()
|
||||
})
|
||||
@@ -441,4 +450,208 @@ describe("applyAgentConfig builtin override protection", () => {
|
||||
]),
|
||||
)
|
||||
})
|
||||
|
||||
describe("agent_definitions and opencode.json integration", () => {
|
||||
test("agent_definitions agents appear in output", async () => {
|
||||
// given
|
||||
loadAgentDefinitionsSpy.mockReturnValue({
|
||||
"my-custom-agent": {
|
||||
name: "my-custom-agent",
|
||||
prompt: "test custom agent from agent_definitions",
|
||||
mode: "subagent",
|
||||
},
|
||||
})
|
||||
const pluginConfig = createPluginConfig()
|
||||
pluginConfig.agent_definitions = ["/fake/path/agent.md"]
|
||||
|
||||
// when
|
||||
const result = await applyAgentConfig({
|
||||
config: createBaseConfig(),
|
||||
pluginConfig,
|
||||
ctx: { directory: "/tmp" },
|
||||
pluginComponents: createPluginComponents(),
|
||||
})
|
||||
|
||||
// then
|
||||
expect(result["my-custom-agent"]).toBeDefined()
|
||||
expect(result["my-custom-agent"]?.prompt).toBe("test custom agent from agent_definitions")
|
||||
})
|
||||
|
||||
test("opencode.json agents appear in output", async () => {
|
||||
// given
|
||||
readOpencodeConfigAgentsSpy.mockReturnValue({
|
||||
"opencode-agent": {
|
||||
name: "opencode-agent",
|
||||
prompt: "test opencode config agent",
|
||||
mode: "subagent",
|
||||
description: "(opencode-config) OC",
|
||||
},
|
||||
})
|
||||
|
||||
// when
|
||||
const result = await applyAgentConfig({
|
||||
config: createBaseConfig(),
|
||||
pluginConfig: createPluginConfig(),
|
||||
ctx: { directory: "/tmp" },
|
||||
pluginComponents: createPluginComponents(),
|
||||
})
|
||||
|
||||
// then
|
||||
expect(result["opencode-agent"]).toBeDefined()
|
||||
expect(result["opencode-agent"]?.prompt).toBe("test opencode config agent")
|
||||
expect(result["opencode-agent"]?.description).toBe("(opencode-config) OC")
|
||||
})
|
||||
|
||||
test("agent_definitions agents subject to disabled_agents filtering", async () => {
|
||||
// given
|
||||
loadAgentDefinitionsSpy.mockReturnValue({
|
||||
"disabled-custom-agent": {
|
||||
name: "disabled-custom-agent",
|
||||
prompt: "this should be filtered",
|
||||
mode: "subagent",
|
||||
},
|
||||
})
|
||||
const pluginConfig = createPluginConfig()
|
||||
pluginConfig.agent_definitions = ["/fake/path/agent.md"]
|
||||
pluginConfig.disabled_agents = ["disabled-custom-agent"]
|
||||
|
||||
// when
|
||||
const result = await applyAgentConfig({
|
||||
config: createBaseConfig(),
|
||||
pluginConfig,
|
||||
ctx: { directory: "/tmp" },
|
||||
pluginComponents: createPluginComponents(),
|
||||
})
|
||||
|
||||
// then
|
||||
expect(result["disabled-custom-agent"]).toBeUndefined()
|
||||
})
|
||||
|
||||
test("agent_definitions cannot override builtin agents", async () => {
|
||||
// given
|
||||
loadAgentDefinitionsSpy.mockReturnValue({
|
||||
oracle: {
|
||||
name: "oracle",
|
||||
prompt: "evil override prompt",
|
||||
mode: "subagent",
|
||||
},
|
||||
})
|
||||
const pluginConfig = createPluginConfig()
|
||||
pluginConfig.agent_definitions = ["/fake/path/agent.md"]
|
||||
|
||||
// when
|
||||
const result = await applyAgentConfig({
|
||||
config: createBaseConfig(),
|
||||
pluginConfig,
|
||||
ctx: { directory: "/tmp" },
|
||||
pluginComponents: createPluginComponents(),
|
||||
})
|
||||
|
||||
// then
|
||||
expect(result.oracle).toBeDefined()
|
||||
expect(result.oracle?.prompt).not.toBe("evil override prompt")
|
||||
})
|
||||
|
||||
test("precedence: configAgents override agent_definitions", async () => {
|
||||
// given
|
||||
loadAgentDefinitionsSpy.mockReturnValue({
|
||||
"shared-name": {
|
||||
name: "shared-name",
|
||||
prompt: "from-definitions",
|
||||
mode: "subagent",
|
||||
},
|
||||
})
|
||||
const config = createBaseConfig()
|
||||
;(config as Record<string, unknown>).agent = {
|
||||
"shared-name": {
|
||||
name: "shared-name",
|
||||
prompt: "from-config",
|
||||
mode: "subagent",
|
||||
},
|
||||
}
|
||||
const pluginConfig = createPluginConfig()
|
||||
pluginConfig.agent_definitions = ["/fake/path/agent.md"]
|
||||
|
||||
// when
|
||||
const result = await applyAgentConfig({
|
||||
config,
|
||||
pluginConfig,
|
||||
ctx: { directory: "/tmp" },
|
||||
pluginComponents: createPluginComponents(),
|
||||
})
|
||||
|
||||
// then
|
||||
expect(result["shared-name"]).toBeDefined()
|
||||
expect(result["shared-name"]?.prompt).toBe("from-config")
|
||||
})
|
||||
|
||||
test("precedence: agent_definitions overrides project agents", async () => {
|
||||
// given
|
||||
loadProjectAgentsSpy.mockReturnValue({
|
||||
"shared-name": {
|
||||
name: "shared-name",
|
||||
prompt: "from-project",
|
||||
mode: "subagent",
|
||||
},
|
||||
})
|
||||
loadAgentDefinitionsSpy.mockReturnValue({
|
||||
"shared-name": {
|
||||
name: "shared-name",
|
||||
prompt: "from-definitions",
|
||||
mode: "subagent",
|
||||
},
|
||||
})
|
||||
const pluginConfig = createPluginConfig()
|
||||
pluginConfig.agent_definitions = ["/fake/path/agent.md"]
|
||||
|
||||
// when
|
||||
const result = await applyAgentConfig({
|
||||
config: createBaseConfig(),
|
||||
pluginConfig,
|
||||
ctx: { directory: "/tmp" },
|
||||
pluginComponents: createPluginComponents(),
|
||||
})
|
||||
|
||||
// then
|
||||
expect(result["shared-name"]).toBeDefined()
|
||||
expect(result["shared-name"]?.prompt).toBe("from-definitions")
|
||||
})
|
||||
|
||||
test("both Sisyphus-enabled and disabled paths include new sources", async () => {
|
||||
// given
|
||||
loadAgentDefinitionsSpy.mockReturnValue({
|
||||
"definitions-agent": {
|
||||
name: "definitions-agent",
|
||||
prompt: "from agent_definitions",
|
||||
mode: "subagent",
|
||||
},
|
||||
})
|
||||
readOpencodeConfigAgentsSpy.mockReturnValue({
|
||||
"opencode-agent": {
|
||||
name: "opencode-agent",
|
||||
prompt: "from opencode.json",
|
||||
mode: "subagent",
|
||||
},
|
||||
})
|
||||
const pluginConfig = createPluginConfig()
|
||||
pluginConfig.agent_definitions = ["/fake/path/agent.md"]
|
||||
if (pluginConfig.sisyphus_agent) {
|
||||
pluginConfig.sisyphus_agent.planner_enabled = false
|
||||
}
|
||||
|
||||
// when
|
||||
const result = await applyAgentConfig({
|
||||
config: createBaseConfig(),
|
||||
pluginConfig,
|
||||
ctx: { directory: "/tmp" },
|
||||
pluginComponents: createPluginComponents(),
|
||||
})
|
||||
|
||||
// then
|
||||
expect(result["definitions-agent"]).toBeDefined()
|
||||
expect(result["definitions-agent"]?.prompt).toBe("from agent_definitions")
|
||||
expect(result["opencode-agent"]).toBeDefined()
|
||||
expect(result["opencode-agent"]?.prompt).toBe("from opencode.json")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user