diff --git a/packages/builtin-tools/src/tools/TeamCreateTool/TeamCreateTool.ts b/packages/builtin-tools/src/tools/TeamCreateTool/TeamCreateTool.ts index 3a2f8aee..138b9137 100644 --- a/packages/builtin-tools/src/tools/TeamCreateTool/TeamCreateTool.ts +++ b/packages/builtin-tools/src/tools/TeamCreateTool/TeamCreateTool.ts @@ -53,6 +53,8 @@ export type Output = { team_name: string team_file_path: string lead_agent_id: string + lead_agent_status: string + next_step: string } export type Input = z.infer @@ -106,7 +108,8 @@ export const TeamCreateTool: Tool = buildTool({ }, async description() { - return 'Create a new team for coordinating multiple agents' + return 'Create a new team with an automatically-started lead agent. ' + + 'The lead already exists after this call — never spawn it with Agent.' }, async prompt() { @@ -239,6 +242,8 @@ export const TeamCreateTool: Tool = buildTool({ team_name: finalTeamName, team_file_path: teamFilePath, lead_agent_id: leadAgentId, + lead_agent_status: `already running — created by TeamCreate, do not spawn`, + next_step: `SendMessage(to: "${leadAgentId}"). Use Agent only for additional teammates.`, }, } }, diff --git a/packages/builtin-tools/src/tools/TeamCreateTool/prompt.ts b/packages/builtin-tools/src/tools/TeamCreateTool/prompt.ts index dddf6fb1..523cc281 100644 --- a/packages/builtin-tools/src/tools/TeamCreateTool/prompt.ts +++ b/packages/builtin-tools/src/tools/TeamCreateTool/prompt.ts @@ -38,12 +38,24 @@ This creates: 1. **Create a team** with TeamCreate - this creates both the team and its task list 2. **Create tasks** using the Task tools (TaskCreate, TaskList, etc.) - they automatically use the team's task list -3. **Spawn teammates** using the Agent tool with \`team_name\` and \`name\` parameters to create teammates that join the team +3. **Spawn teammates** (beyond the lead) using the Agent tool with \`team_name\` and \`name\` parameters — but only when the task genuinely needs more than one agent. **TeamCreate already creates the lead (\`team-lead@\`)** — do NOT pass \`name: "team-lead"\` to the Agent tool; that creates a duplicate agent, not the lead. Talk to the lead with SendMessage using the returned lead_agent_id. 4. **Assign tasks** using TaskUpdate with \`owner\` to give tasks to idle teammates 5. **Teammates work on assigned tasks** and mark them completed via TaskUpdate 6. **Teammates go idle between turns** - after each turn, teammates automatically go idle and send a notification. IMPORTANT: Be patient with idle teammates! Don't comment on their idleness until it actually impacts your work. 7. **Shutdown your team** - when the task is completed, gracefully shut down your teammates via SendMessage with \`message: {type: "shutdown_request"}\`. +## Minimal Lifecycle (Single-Agent Task) + +When the task needs only one agent (not a multi-agent parallel workload), do NOT spawn additional teammates. The lead agent created by TeamCreate is sufficient: + +\`\`\` +1. TeamCreate(team_name: "my-sweep") +2. SendMessage(to: , ...) — the lead receives and processes your instructions +3. TeamDelete(team_name: "my-sweep") +\`\`\` + +Use the Agent tool only to add teammates BEYOND the lead, and only when the task genuinely needs multiple agents working in parallel. + ## Task Ownership Tasks are assigned using TaskUpdate with the \`owner\` parameter. Any agent can set or change task ownership via TaskUpdate. diff --git a/packages/builtin-tools/src/tools/shared/spawnMultiAgent.ts b/packages/builtin-tools/src/tools/shared/spawnMultiAgent.ts index 78d81102..6dd193da 100644 --- a/packages/builtin-tools/src/tools/shared/spawnMultiAgent.ts +++ b/packages/builtin-tools/src/tools/shared/spawnMultiAgent.ts @@ -279,6 +279,17 @@ export async function generateUniqueTeammateName( const existingNames = new Set(teamFile.members.map(m => m.name.toLowerCase())) + // If the caller tries to spawn "team-lead" which already exists (created by + // TeamCreate), fail with a redirect — silently deduping to "team-lead-2" + // would cascade into wrong SendMessage recipients and blocked TeamDelete. + if (baseName.toLowerCase() === TEAM_LEAD_NAME && existingNames.has(TEAM_LEAD_NAME)) { + throw new Error( + `"${TEAM_LEAD_NAME}" already exists in team "${teamName}" — it was created by TeamCreate. ` + + `Use SendMessage to communicate with the lead. ` + + `To add a different teammate, choose another name.`, + ) + } + // If the base name doesn't exist, use it as-is if (!existingNames.has(baseName.toLowerCase())) { return baseName