CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
team-create.ts323 linesDownload Raw Back to tools
1/**2 * @license3 * Copyright 2025 Qwen4 * SPDX-License-Identifier: Apache-2.05 */6 7/**8 * team_create tool — creates a new agent team.9 */10 11import type { ToolInvocation, ToolResult, TeamResultDisplay } from './tools.js';12import { BaseDeclarativeTool, BaseToolInvocation, Kind } from './tools.js';13import { ToolNames, ToolDisplayNames } from './tool-names.js';14import type { Config } from '../config/config.js';15import {16  sanitizeName,17  formatAgentId,18  createTeamFile,19  getTeamDir,20  getTasksDir,21  tryReclaimStaleTeam,22} from '../agents/team/teamHelpers.js';23import { resetTaskList } from '../agents/team/tasks.js';24import { clearAllInboxes } from '../agents/team/mailbox.js';25import { TeamManager } from '../agents/team/TeamManager.js';26import { InProcessBackend } from '../agents/backends/InProcessBackend.js';27import { isNodeError } from '../utils/errors.js';28import type { TeamFile, TeamContext } from '../agents/team/types.js';29import { LEADER_NAME, MAX_TEAMMATES } from '../agents/team/types.js';30 31export interface TeamCreateParams {32  team_name: string;33  description?: string;34}35 36class TeamCreateInvocation extends BaseToolInvocation<37  TeamCreateParams,38  ToolResult39> {40  constructor(41    private config: Config,42    params: TeamCreateParams,43  ) {44    super(params);45  }46 47  getDescription(): string {48    return `Create team "${this.params.team_name}"`;49  }50 51  async execute(): Promise<ToolResult> {52    const teamName = sanitizeName(this.params.team_name);53    if (!teamName) {54      const msg = 'Team name is required.';55      return {56        llmContent: msg,57        returnDisplay: msg,58        error: { message: msg },59      };60    }61 62    // Mutual exclusion: Team and Arena cannot coexist.63    if (this.config.getArenaManager()) {64      const msg =65        'Cannot create a team while an Arena session is active. ' +66        'End the Arena session first.';67      return {68        llmContent: msg,69        returnDisplay: msg,70        error: { message: msg },71      };72    }73 74    // Prevent creating a second team.75    if (this.config.getTeamManager()) {76      const msg =77        'A team is already active. Delete it before ' + 'creating a new one.';78      return {79        llmContent: msg,80        returnDisplay: msg,81        error: { message: msg },82      };83    }84 85    // Build team file. The owner identity (session UUID + PID) is what86    // lets a later `team_create` distinguish "name in use by a live87    // session" from "stranded by an exit that never ran team_delete".88    const leadAgentId = formatAgentId(LEADER_NAME, teamName);89    const teamFile: TeamFile = {90      name: teamName,91      description: this.params.description,92      createdAt: Date.now(),93      leadAgentId,94      leadSessionId: this.config.getSessionId(),95      leadPid: process.pid,96      members: [],97    };98 99    // Atomically create the team file. EEXIST means another team file100    // holds this name — either a live concurrent session (the101    // in-process guard above only checks the current Config) or a102    // stale leftover: nothing deletes team dirs on normal exit, so103    // every Ctrl+C / completed headless run / crash strands the name.104    // Reclaim the stale case via the recorded leadPid and retry once;105    // only a live owner (or an unverifiable pre-leadPid file) keeps106    // the name wedged.107    try {108      await createTeamFile(teamName, teamFile);109    } catch (err) {110      if (isNodeError(err) && err.code === 'EEXIST') {111        const reclaimed = await tryReclaimStaleTeam(teamName);112        if (!reclaimed) {113          const msg =114            `Team "${teamName}" already exists and appears to be ` +115            `owned by a live qwen-code session. Pick a different ` +116            `name, or — if you're sure no other session is using ` +117            `it — remove the on-disk artifacts manually:\n` +118            `  rm -rf "${getTeamDir(teamName)}" "${getTasksDir(teamName)}"`;119          return {120            llmContent: msg,121            returnDisplay: msg,122            error: { message: msg },123          };124        }125        // Stale team reclaimed — retry the exclusive create. A loss126        // here means a concurrent creator won the race; let it throw.127        await createTeamFile(teamName, teamFile);128      } else {129        throw err;130      }131    }132 133    // Reset tasks and inboxes only after the team file is ours.134    await resetTaskList(teamName);135    await clearAllInboxes(teamName);136 137    // Create backend and manager.138    const backend = new InProcessBackend(this.config);139    await backend.init();140    const manager = new TeamManager(141      backend,142      teamFile,143      this.config.getSubagentManager(),144      {145        maxTeammates: this.config.getAgentsSettings().team?.maxTeammates,146      },147    );148 149    // Set on config so other tools can find it.150    this.config.setTeamManager(manager);151 152    const ctx: TeamContext = {153      teamName,154      leadAgentId,155      teammates: {},156    };157    this.config.setTeamContext(ctx);158 159    // No leader approval bridge is registered here. Teammate160    // tool approvals surface through each teammate's own161    // pendingApprovals map, which the interactive UI renders162    // in the teammate's tab (AgentChatView). The bridge163    // (leaderPermissionBridge) is available for future use if164    // approvals need to appear in the leader's context too.165 166    const display: TeamResultDisplay = {167      type: 'team_result',168      teamName,169      action: 'created',170    };171    const llmContent =172      `Team "${teamName}" created.` +173      (this.params.description174        ? ` Description: ${this.params.description}`175        : '');176    return { llmContent, returnDisplay: display };177  }178}179 180export class TeamCreateTool extends BaseDeclarativeTool<181  TeamCreateParams,182  ToolResult183> {184  static readonly Name = ToolNames.TEAM_CREATE;185 186  constructor(private config: Config) {187    super(188      TeamCreateTool.Name,189      ToolDisplayNames.TEAM_CREATE,190      `# TeamCreate191 192## When to Use193 194Use this tool proactively whenever:195- The user explicitly asks to use a team, swarm, or group of agents196- The user mentions wanting agents to work together, coordinate, or collaborate197- A task is complex enough that it would benefit from parallel work by multiple agents (e.g., building a full-stack feature with frontend and backend work, refactoring a codebase while keeping tests passing, implementing a multi-step project with research, planning, and coding phases)198 199When in doubt about whether a task warrants a team, prefer spawning a team.200 201## Choosing Agent Types for Teammates202 203When spawning teammates via the Agent tool, choose the \`subagent_type\` based on what tools the agent needs for its task. Each agent type has a different set of available tools — match the agent to the work:204 205- **Read-only agents** (e.g., Explore, Plan) cannot edit or write files. Only assign them research, search, or planning tasks. Never assign them implementation work.206- **Full-capability agents** (e.g., general-purpose) have access to all tools including file editing, writing, and bash. Use these for tasks that require making changes.207- **Custom agents** defined in \`.qwen/agents/\` may have their own tool restrictions. Check their descriptions to understand what they can and cannot do.208 209Always review the agent type descriptions and their available tools listed in the Agent tool prompt before selecting a \`subagent_type\` for a teammate.210 211Create a new team to coordinate multiple agents working on a project. Teams have a 1:1 correspondence with task lists (Team = TaskList).212 213\`\`\`214{215  "team_name": "my-project",216  "description": "Working on feature X"217}218\`\`\`219 220This creates:221- A team file at \`~/.qwen/teams/{team-name}/config.json\`222- A corresponding task list directory at \`~/.qwen/tasks/{team-name}/\`223 224## Team Workflow225 2261. **Create a team** with TeamCreate - this creates both the team and its task list2272. **Create tasks** using the Task tools (TaskCreate, TaskList, etc.) - they automatically use the team's task list2283. **Spawn teammates** using the Agent tool with the \`name\` parameter to create teammates that join the active team (max ${config.getAgentsSettings().team?.maxTeammates ?? MAX_TEAMMATES} teammates per team)2294. **Assign tasks** using TaskUpdate with \`owner\` to give tasks to idle teammates2305. **Teammates work on assigned tasks** and mark them completed via TaskUpdate2316. **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.2327. **Shutdown your team** - when the task is completed, gracefully shut down your teammates via SendMessage with \`type: "shutdown_request"\` (a top-level parameter alongside \`to\` and \`message\`).233 234## Task Ownership235 236Tasks are assigned using TaskUpdate with the \`owner\` parameter. Any agent can set or change task ownership via TaskUpdate.237 238## Automatic Message Delivery239 240**IMPORTANT**: Messages from teammates are automatically delivered to you. You do NOT need to manually check your inbox.241 242When you spawn teammates:243- They will send you messages when they complete tasks or need help244- These messages appear automatically as new conversation turns (like user messages)245- If you're busy (mid-turn), messages are queued and delivered when your turn ends246- The UI shows a brief notification with the sender's name when messages are waiting247 248Messages will be delivered automatically.249 250When reporting on teammate messages, you do NOT need to quote the original message—it's already rendered to the user.251 252## Teammate Idle State253 254Teammates go idle after every turn—this is completely normal and expected. A teammate going idle immediately after sending you a message does NOT mean they are done or unavailable. Idle simply means they are waiting for input.255 256- **Idle teammates can receive messages.** Sending a message to an idle teammate wakes them up and they will process it normally.257- **Idle notifications are automatic.** The system sends an idle notification whenever a teammate's turn ends. You do not need to react to idle notifications unless you want to assign new work or send a follow-up message.258- **Do not treat idle as an error.** A teammate sending a message and then going idle is the normal flow—they sent their message and are now waiting for a response.259- **Peer DM visibility.** When a teammate sends a DM to another teammate, a brief summary is included in their idle notification. This gives you visibility into peer collaboration without the full message content. You do not need to respond to these summaries — they are informational.260 261## Discovering Team Members262 263Teammates can read the team config file to discover other team members:264- **Team config location**: \`~/.qwen/teams/{team-name}/config.json\`265 266The config file contains a \`members\` array with each teammate's:267- \`name\`: Human-readable name (**always use this** for messaging and task assignment)268- \`agentId\`: Unique identifier (for reference only - do not use for communication)269- \`agentType\`: Role/type of the agent270 271**IMPORTANT**: Always refer to teammates by their NAME (e.g., "team-lead", "researcher", "tester"). Names are used for:272- \`to\` when sending messages273- Identifying task owners274 275Example of reading team config:276\`\`\`277Use the Read tool to read ~/.qwen/teams/{team-name}/config.json278\`\`\`279 280## Task List Coordination281 282Teams share a task list that all teammates can access at \`~/.qwen/tasks/{team-name}/\`.283 284Teammates should:2851. Check TaskList periodically, **especially after completing each task**, to find available work or see newly unblocked tasks2862. Claim unassigned, unblocked tasks with TaskUpdate (set \`owner\` to your name). **Prefer tasks in ID order** (lowest ID first) when multiple tasks are available, as earlier tasks often set up context for later ones2873. Create new tasks with \`TaskCreate\` when identifying additional work2884. Mark tasks as completed with \`TaskUpdate\` when done, then check TaskList for next work2895. Coordinate with other teammates by reading the task list status2906. If all available tasks are blocked, notify the team lead or help resolve blocking tasks291 292**IMPORTANT notes for communication with your team**:293- Do not use terminal tools to view your team's activity; always send a message to your teammates (and remember, refer to them by name).294- Your team cannot hear you if you do not use the SendMessage tool. Always send a message to your teammates if you are responding to them.295- Do NOT send structured JSON status messages like \`{"type":"idle",...}\` or \`{"type":"task_completed",...}\`. Just communicate in plain text when you need to message teammates.296- Use TaskUpdate to mark tasks completed.297- If you are an agent in the team, the system will automatically send idle notifications to the team lead when you stop.`,298      Kind.Other,299      {300        type: 'object',301        properties: {302          team_name: {303            type: 'string',304            description: 'Name for the team (alphanumeric and hyphens).',305          },306          description: {307            type: 'string',308            description: 'Optional description of the team.',309          },310        },311        required: ['team_name'],312        additionalProperties: false,313      },314    );315  }316 317  protected createInvocation(318    params: TeamCreateParams,319  ): ToolInvocation<TeamCreateParams, ToolResult> {320    return new TeamCreateInvocation(this.config, params);321  }322}323 
basant307/AI_Governance_Project · CoolFace