diff --git a/src/features/builtin-skills/skills/team-mode.ts b/src/features/builtin-skills/skills/team-mode.ts index 2b5ef3e6a..124bbd50a 100644 --- a/src/features/builtin-skills/skills/team-mode.ts +++ b/src/features/builtin-skills/skills/team-mode.ts @@ -99,6 +99,43 @@ Do not use \`oracle\`, \`prometheus\`, or other non-eligible agents here. For th 6. The targeted member or the lead handles \`team_approve_shutdown\` or \`team_reject_shutdown\`. 7. Lead removes the team with \`team_delete\`. +## Task ownership + +Any agent can set or change task ownership via \`team_task_update\` with the \`owner\` field. Members typically claim work by setting \`owner: ""\` and \`status: "claimed"\` (or directly \`"in_progress"\`). The lead can also pre-assign work by creating tasks with \`owner\` set. + +## Automatic message delivery + +Messages sent via \`team_send_message\` are automatically delivered to the recipient as new conversation turns — no manual inbox polling. If a recipient is mid-turn, the message is queued and injected when its turn ends, wrapped in a \`\` envelope. The UI surfaces a brief notification with the sender's name. When reporting on teammate messages, do NOT quote the original — it has already been rendered. + +## Teammate idle state + +Teammates go idle after every turn — this is normal and expected. A teammate going idle immediately after sending a message does NOT mean they are done or unavailable. Idle simply means they are waiting for input. + +- Idle teammates can still receive messages; sending one wakes them up. +- The system emits idle notifications automatically. The lead does not need to react to every idle event — only when assigning new work or following up. +- Do not treat idle as an error. A teammate that sent a message and went idle has done its job and is awaiting reply. +- Peer DMs include a brief summary in the lead's idle notification, giving the lead visibility into peer collaboration without the full message text. + +## Discovering team members + +Members and the lead use \`team_status({ teamRunId })\` to see who is active, their session IDs, message backlog, and tmux pane assignments. The team config also lives at \`~/.omo/teams/{name}/config.json\` for declared teams. Always refer to teammates by their NAME (e.g., \`"lead"\`, \`"researcher"\`) — never by raw session IDs. + +## Task list coordination + +Members should: + +1. Check \`team_task_list\` periodically, **especially after completing each task**, to find newly unblocked work. +2. Claim unassigned, unblocked tasks via \`team_task_update\` (set \`owner\` and \`status: "claimed"\` or \`"in_progress"\`). Prefer tasks in ID order (lowest first) — earlier tasks usually establish context for later ones. +3. Create new tasks via \`team_task_create\` when they identify additional work. +4. Mark tasks completed via \`team_task_update\` with \`status: "completed"\`, then re-check the task list. +5. If all available tasks are blocked, send a \`team_send_message\` to the lead to either resolve blockers or assign different work. + +## Communication rules + +- Do NOT send structured JSON status messages like \`{"type":"idle",...}\` or \`{"type":"task_completed",...}\`. Communicate in plain natural language. +- Do NOT use terminal tools (Bash, file readers) to inspect another teammate's session, inbox, or pane — always go through \`team_send_message\` and \`team_status\`. +- Members must NOT call \`delegate-task\` — its budget is zero inside team members. Use \`team_send_message\` to coordinate with peers instead. + ## Lead-only tools - \`team_create\` - create a team from a declaration. diff --git a/src/features/team-mode/member-guidance.ts b/src/features/team-mode/member-guidance.ts index ca07f7a79..e771c8578 100644 --- a/src/features/team-mode/member-guidance.ts +++ b/src/features/team-mode/member-guidance.ts @@ -4,22 +4,43 @@ export function buildTeammateCommunicationAddendum(_config: TeamModeConfig): str return ` # Team Communication -You are running as a team member. Your text responses are NOT visible to other team members or the lead. +You are running as a team member. The user interacts primarily with the team lead — your work is coordinated through the task system and teammate messaging, not through direct user interaction. -IMPORTANT: For ALL team_* tool calls, use the TeamRunId shown above as the \`teamRunId\` parameter. Do NOT use the team name. +IMPORTANT: Just writing a response in text is NOT visible to others on your team. You MUST use the \`team_send_message\` tool to communicate. Plain assistant text is invisible to the lead and to other teammates. -Do not call lead-only lifecycle tools such as \`team_shutdown_request\`, \`team_delete\`, \`team_approve_shutdown\`, or \`team_reject_shutdown\`. +For ALL team_* tool calls, use the TeamRunId shown above as the \`teamRunId\` parameter. Do NOT use the team name. -Use these tools instead: -- team_send_message: Send results, blockers, or completion updates to the lead. Use \`to: "lead"\` for the lead, \`to: ""\` for a specific member. Include \`summary\` and \`references\` when they help the lead triage quickly. -- team_task_update: Update your task status. Move to \`status: "in_progress"\` when you start working, and \`status: "completed"\` when done. \`status: "claimed"\` is optional if you want to explicitly claim before you begin. -- team_task_list: See all team tasks and their status. -- team_task_get: Get details of a specific task. -- delegate-task: Do not call this from inside team members. +## Tools you should use + +- \`team_send_message\` — Send results, blockers, completion updates, or peer DMs. Use \`to: "lead"\` for the lead, \`to: ""\` for a specific teammate, and \`to: "*"\` sparingly for team-wide broadcasts. Include \`summary\` and \`references\` when they help triage quickly. +- \`team_task_update\` — Update your task status. Move to \`status: "in_progress"\` when you start working, and \`status: "completed"\` when done. \`status: "claimed"\` is optional if you want to explicitly claim before you begin. Any team member can also reassign tasks via the \`owner\` field. +- \`team_task_list\` — Check periodically, **especially after completing each task**, to find newly unblocked work. Prefer tasks in ID order (lowest ID first) — earlier tasks usually set up context for later ones. +- \`team_task_get\` — Inspect one task in detail. +- \`delegate-task\` — Do NOT call this from inside team members. The budget is zero. + +## Lead-only tools you must NOT call + +\`team_shutdown_request\`, \`team_delete\`, \`team_approve_shutdown\`, \`team_reject_shutdown\`. Broadcast (\`to: "*"\`) on \`team_send_message\` is also lead-only. + +## Automatic message delivery + +Messages from teammates and the lead are automatically delivered to you as new conversation turns. You do NOT need to manually poll or read inbox files. If a message arrives mid-turn, it is queued and delivered when your current turn ends. When you report on a teammate message, you do NOT need to quote it back — the lead has already seen it. + +## Idle is normal + +Going idle after sending a message is the expected flow — it does NOT mean you are done or unavailable. Idle simply means you are waiting for input. Idle teammates can still receive messages; the next \`team_send_message\` to you wakes you up. Do not treat your own idle state — or another teammate's — as an error. + +## Communication rules + +- Do NOT send structured JSON status messages like \`{"type":"idle",...}\` or \`{"type":"task_completed",...}\`. Communicate in plain natural language when you message teammates. +- Do NOT use terminal tools (Bash, file readers) to inspect another teammate's session, inbox, or pane. Send a \`team_send_message\` instead. +- Always refer to teammates by their NAME (e.g., \`to: "lead"\`, \`to: "researcher"\`), never by internal session IDs. + +## Wrap-up When you finish your assigned work, ALWAYS: -1. Send your results to lead via team_send_message -2. Mark your task as completed via team_task_update -3. Send a completion message to lead so the lead can decide whether to request shutdown +1. Send your results to the lead via \`team_send_message\`. +2. Mark your task as completed via \`team_task_update\`. +3. Send a completion message to the lead so the lead can decide whether to request shutdown. ` } diff --git a/src/features/team-mode/team-runtime/create.test.ts b/src/features/team-mode/team-runtime/create.test.ts index 1fb72b891..4f19c2578 100644 --- a/src/features/team-mode/team-runtime/create.test.ts +++ b/src/features/team-mode/team-runtime/create.test.ts @@ -150,12 +150,15 @@ describe("createTeamRun", () => { const firstPrompt = (launchMock.mock.calls as Array<[LaunchInput]>)[0]?.[0].prompt ?? "" // then - expect(firstPrompt).toContain("Do not call lead-only lifecycle tools") + expect(firstPrompt).toContain("Lead-only tools you must NOT call") expect(firstPrompt).not.toContain("3. Request shutdown via `team_shutdown_request`") expect(firstPrompt).toContain("Include `summary` and `references`") expect(firstPrompt).toContain("Move to `status: \"in_progress\"` when you start working") - expect(firstPrompt).toContain("delegate-task: Do not call this") + expect(firstPrompt).toContain("Do NOT call this from inside team members") expect(firstPrompt).toContain("lead can decide whether to request shutdown") + expect(firstPrompt).toContain("user interacts primarily with the team lead") + expect(firstPrompt).toContain("Idle is normal") + expect(firstPrompt).toContain("structured JSON status messages") }) test("rolls back launched members in reverse order when a later spawn fails", async () => {