fix(agents): stop advertising custom agents to orchestrators
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -26,7 +26,6 @@ import { collectPendingBuiltinAgents } from "./builtin-agents/general-agents"
|
|||||||
import { maybeCreateSisyphusConfig } from "./builtin-agents/sisyphus-agent"
|
import { maybeCreateSisyphusConfig } from "./builtin-agents/sisyphus-agent"
|
||||||
import { maybeCreateHephaestusConfig } from "./builtin-agents/hephaestus-agent"
|
import { maybeCreateHephaestusConfig } from "./builtin-agents/hephaestus-agent"
|
||||||
import { maybeCreateAtlasConfig } from "./builtin-agents/atlas-agent"
|
import { maybeCreateAtlasConfig } from "./builtin-agents/atlas-agent"
|
||||||
import { buildCustomAgentMetadata, parseRegisteredAgentSummaries } from "./custom-agent-summaries"
|
|
||||||
|
|
||||||
type AgentSource = AgentFactory | AgentConfig
|
type AgentSource = AgentFactory | AgentConfig
|
||||||
|
|
||||||
@@ -120,23 +119,6 @@ export async function createBuiltinAgents(
|
|||||||
disableOmoEnv,
|
disableOmoEnv,
|
||||||
})
|
})
|
||||||
|
|
||||||
const registeredAgents = parseRegisteredAgentSummaries(customAgentSummaries)
|
|
||||||
const builtinAgentNames = new Set(Object.keys(agentSources).map((name) => name.toLowerCase()))
|
|
||||||
const disabledAgentNames = new Set(disabledAgents.map((name) => name.toLowerCase()))
|
|
||||||
|
|
||||||
for (const agent of registeredAgents) {
|
|
||||||
const lowerName = agent.name.toLowerCase()
|
|
||||||
if (builtinAgentNames.has(lowerName)) continue
|
|
||||||
if (disabledAgentNames.has(lowerName)) continue
|
|
||||||
if (availableAgents.some((availableAgent) => availableAgent.name.toLowerCase() === lowerName)) continue
|
|
||||||
|
|
||||||
availableAgents.push({
|
|
||||||
name: agent.name,
|
|
||||||
description: agent.description,
|
|
||||||
metadata: buildCustomAgentMetadata(agent.name, agent.description),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const sisyphusConfig = maybeCreateSisyphusConfig({
|
const sisyphusConfig = maybeCreateSisyphusConfig({
|
||||||
disabledAgents,
|
disabledAgents,
|
||||||
agentOverrides,
|
agentOverrides,
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { describe, expect, spyOn, test } from "bun:test"
|
||||||
|
import { createBuiltinAgents } from "./builtin-agents"
|
||||||
|
import * as shared from "../shared"
|
||||||
|
|
||||||
|
const TEST_DEFAULT_MODEL = "anthropic/claude-opus-4-6"
|
||||||
|
|
||||||
|
describe("createBuiltinAgents custom agent visibility", () => {
|
||||||
|
test("#given runtime custom agents #when orchestrator prompts are built #then custom agents are not advertised for automatic delegation", async () => {
|
||||||
|
//#given
|
||||||
|
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
|
||||||
|
new Set(["anthropic/claude-opus-4-6", "openai/gpt-5.4"])
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
//#when
|
||||||
|
const agents = await createBuiltinAgents(
|
||||||
|
[],
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
TEST_DEFAULT_MODEL,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: "backend-engineer",
|
||||||
|
description: "Custom backend specialist",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
//#then
|
||||||
|
expect(agents.sisyphus.prompt).not.toContain("backend-engineer")
|
||||||
|
expect(agents.hephaestus.prompt).not.toContain("backend-engineer")
|
||||||
|
expect(agents.atlas.prompt).not.toContain("backend-engineer")
|
||||||
|
} finally {
|
||||||
|
fetchSpy.mockRestore()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -278,7 +278,7 @@ describe("createBuiltinAgents with model overrides", () => {
|
|||||||
fetchSpy.mockRestore()
|
fetchSpy.mockRestore()
|
||||||
})
|
})
|
||||||
|
|
||||||
test("includes custom agents in orchestrator prompts when provided via config", async () => {
|
test("does not advertise custom agents in orchestrator prompts when provided via config", async () => {
|
||||||
// #given
|
// #given
|
||||||
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
|
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
|
||||||
new Set([
|
new Set([
|
||||||
@@ -313,9 +313,9 @@ describe("createBuiltinAgents with model overrides", () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// #then
|
// #then
|
||||||
expect(agents.sisyphus.prompt).toContain("researcher")
|
expect(agents.sisyphus.prompt).not.toContain("researcher")
|
||||||
expect(agents.hephaestus.prompt).toContain("researcher")
|
expect(agents.hephaestus.prompt).not.toContain("researcher")
|
||||||
expect(agents.atlas.prompt).toContain("researcher")
|
expect(agents.atlas.prompt).not.toContain("researcher")
|
||||||
} finally {
|
} finally {
|
||||||
fetchSpy.mockRestore()
|
fetchSpy.mockRestore()
|
||||||
}
|
}
|
||||||
@@ -429,7 +429,7 @@ describe("createBuiltinAgents with model overrides", () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
test("deduplicates custom agents case-insensitively", async () => {
|
test("does not advertise duplicate custom agents case-insensitively", async () => {
|
||||||
// #given
|
// #given
|
||||||
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
|
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
|
||||||
new Set(["anthropic/claude-opus-4-6", "openai/gpt-5.4"])
|
new Set(["anthropic/claude-opus-4-6", "openai/gpt-5.4"])
|
||||||
@@ -455,13 +455,13 @@ describe("createBuiltinAgents with model overrides", () => {
|
|||||||
|
|
||||||
// #then
|
// #then
|
||||||
const matches = (agents.sisyphus?.prompt ?? "").match(/Custom agent: researcher/gi) ?? []
|
const matches = (agents.sisyphus?.prompt ?? "").match(/Custom agent: researcher/gi) ?? []
|
||||||
expect(matches.length).toBe(1)
|
expect(matches.length).toBe(0)
|
||||||
} finally {
|
} finally {
|
||||||
fetchSpy.mockRestore()
|
fetchSpy.mockRestore()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
test("sanitizes custom agent strings for markdown tables", async () => {
|
test("does not surface custom agent strings in orchestrator prompts", async () => {
|
||||||
// #given
|
// #given
|
||||||
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
|
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
|
||||||
new Set(["anthropic/claude-opus-4-6", "openai/gpt-5.4"])
|
new Set(["anthropic/claude-opus-4-6", "openai/gpt-5.4"])
|
||||||
@@ -488,7 +488,7 @@ describe("createBuiltinAgents with model overrides", () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// #then
|
// #then
|
||||||
expect(agents.sisyphus.prompt).toContain("Line1 Alpha \\| Beta")
|
expect(agents.sisyphus.prompt).not.toContain("Line1 Alpha \\| Beta")
|
||||||
} finally {
|
} finally {
|
||||||
fetchSpy.mockRestore()
|
fetchSpy.mockRestore()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user