feat(team-mode): align member and lead prompts with Claude Code free-code reference
Bring the team member addendum and the team-mode skill in line with the
Claude Code experimental Agent Teams reference (../free-code) on the
behavioral framing the LLM needs, while keeping every omo-specific
constraint (TeamRunId param, lead-only tool blocklist, eligibility
registry, sisyphus-junior routing) intact.
Member addendum (src/features/team-mode/member-guidance.ts) — adopt
free-code teammatePromptAddendum.ts wisdom that was missing:
- "User interacts primarily with the team lead" — members no longer try
to address the user directly; they coordinate via task system + DMs.
- Stronger emphasis that plain assistant text is invisible to the team;
team_send_message is mandatory, not optional.
- Broadcast (`to: "*"`) called out explicitly with "sparingly" guidance
and the lead-only constraint.
- "Idle is normal" section: idle ≠ done, idle teammates can still
receive messages, the lead must not treat idle as an error.
- "Communication rules" section forbidding structured JSON status
messages and forbidding terminal tools as a peer-inspection backdoor;
always reference teammates by NAME, not session ID.
- Auto-delivery framing: messages arrive as new turns, no manual inbox
polling, no need to quote a teammate's message back to the lead.
Team-mode skill (src/features/builtin-skills/skills/team-mode.ts) —
adopt free-code TeamCreateTool/prompt.ts sections that were missing:
- Task ownership: any agent can set/change `owner` via team_task_update.
- Automatic message delivery: <peer_message ...> envelope flow.
- Teammate idle state: idle is the expected post-turn state; peer DM
summaries surface in the lead's idle notification.
- Discovering team members: team_status + ~/.omo/teams/ config; refer by
name, never raw session IDs.
- Task list coordination: claim by ID order (lowest first), check after
every completion, escalate blocking deadlocks to the lead.
- Communication rules: same anti-patterns as the member addendum, scoped
for the lead's awareness (no JSON status, no terminal peer-inspection,
no delegate-task from inside members).
Test (create.test.ts) updated to track the renamed section header
("Lead-only tools you must NOT call") and to lock the new free-code
parity assertions ("user interacts primarily with the team lead",
"Idle is normal", "structured JSON status messages").
All other contract tests (team-mode skill keyword/section tests,
member parser, runtime tests) untouched and still passing.
Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode).
This commit is contained in:
@@ -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: "<their-name>"\` 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 \`<peer_message ...>\` 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.
|
||||
|
||||
@@ -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: "<name>"\` 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: "<name>"\` 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.
|
||||
`
|
||||
}
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
Reference in New Issue
Block a user