Merge pull request #4065 from PeterPonyu/fix/3987-team-create-hard-reject-validation

fix(team-mode): reject team_create from hard-reject agents (#3987)
This commit is contained in:
YeonGyu-Kim
2026-05-21 00:42:04 +09:00
committed by GitHub
2 changed files with 37 additions and 0 deletions
@@ -284,6 +284,34 @@ describe("team lifecycle tools", () => {
expect(hasRuntime(created.teamRunId)).toBe(false)
})
test("team_create denies a hard-reject caller even when spec has an explicit lead field", async () => {
// given
const teamCreateTool = createTeamCreateToolForTest()
const prometheusContext = {
...createToolContext("lead-session"),
agent: "prometheus",
}
const inlineSpec = {
name: "alpha-team",
lead: { kind: "subagent_type", subagent_type: "sisyphus" },
members: [{ kind: "category", name: "member-a", category: "quick", prompt: "Do the assigned work" }],
}
// when
let errorMessage = ""
try {
await teamCreateTool.execute({ inline_spec: inlineSpec }, prometheusContext)
} catch (error) {
errorMessage = error instanceof Error ? error.message : String(error)
}
// then
expect(errorMessage).toContain("team_create denied")
expect(errorMessage).toContain("prometheus")
expect(errorMessage).toContain("hard-reject")
expect(createTeamRunMock).not.toHaveBeenCalled()
})
test("team_reject_shutdown records the rejection reason", async () => {
// given
const createTool = createTeamCreateToolForTest()
@@ -8,7 +8,9 @@ import { mergeCategories } from "../../../shared/merge-categories"
import type { OpencodeClient } from "../../../tools/delegate-task/types"
import type { BackgroundManager } from "../../background-agent/manager"
import type { TmuxSessionManager } from "../../tmux-subagent/manager"
import { getAgentConfigKey } from "../../../shared/agent-display-names"
import { resolveCallerTeamLead } from "../resolve-caller-team-lead"
import { AGENT_ELIGIBILITY_REGISTRY } from "../types"
import { loadTeamSpec, normalizeTeamSpecInput } from "../team-registry/loader"
import { validateSpec } from "../team-registry/validator"
import { createTeamRun } from "../team-runtime/create"
@@ -197,6 +199,13 @@ export function createTeamCreateTool(
if (!leadSessionId) throw new Error("team_create requires leadSessionId or tool context sessionID")
const projectRoot = typeof runtimeContext.directory === "string" ? runtimeContext.directory : process.cwd()
const callerTeamLead = resolveCallerTeamLead(runtimeContext.agent)
if (callerTeamLead.displayName !== undefined) {
const callerAgentKey = getAgentConfigKey(callerTeamLead.displayName)
const callerRegistryEntry = AGENT_ELIGIBILITY_REGISTRY[callerAgentKey]
if (callerRegistryEntry?.verdict === "hard-reject") {
throw new Error(`team_create denied: caller '${callerAgentKey}' is a hard-reject agent and cannot create teams regardless of an explicit 'lead' in the spec. ${callerRegistryEntry.rejectionMessage ?? `Agent '${callerAgentKey}' is not eligible to lead a team.`}`)
}
}
const defaultCategoryName = resolveDefaultInlineCategory(executorConfig?.userCategories)
const spec = args.teamName
? await deps.loadTeamSpec(args.teamName, config, projectRoot, { callerTeamLead })