From 7413ebfaead899985091b182ba498ddc46d3b60f Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 3 Aug 2026 14:06:41 +0000 Subject: [PATCH] fix(claude-relay-plugin): stop printing workspace keys, use observer tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The three pattern skills each mandated printing the raw workspace key in an observer URL — "Do not print a placeholder — print the real URL the user can click. This is mandatory." A workspace key is an administrative credential; a URL query string is the worst place to put one. They also required the key be copied into every worker prompt. That is unnecessary: `create_workspace` and `set_workspace_key` both pin the workspace to the project, and the relay MCP server a `relay-worker` subagent starts resolves that pin on its own. The mandate put an admin credential into N prompts and N transcripts to achieve nothing. - Step 3 now calls `get_observer_url`, which mints a scoped, expiring, read-only token. Requires the tool added in AgentWorkforce/relay#1422. - Worker prompts no longer carry the workspace key; the worker agent definition and SubagentStart hook now say the workspace is inherited and that a key must never be printed or requested. - The three skills' shared setup steps are now byte-identical, and all three carry the worker/stage tracking table and the "do not self-release" instruction that only relay-team had. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Jmke9G9s7ftrN49opNmdx1 --- .../agents/relay-worker/agent.md | 4 +- .../hooks/subagent-bootstrap.sh | 14 +++-- .../skills/relay-fanout/SKILL.md | 54 ++++++++-------- .../skills/relay-pipeline/SKILL.md | 62 +++++++++---------- .../skills/relay-team/SKILL.md | 53 ++++++++-------- 5 files changed, 92 insertions(+), 95 deletions(-) diff --git a/plugins/claude-relay-plugin/agents/relay-worker/agent.md b/plugins/claude-relay-plugin/agents/relay-worker/agent.md index 8bdc6b5..408e1f6 100644 --- a/plugins/claude-relay-plugin/agents/relay-worker/agent.md +++ b/plugins/claude-relay-plugin/agents/relay-worker/agent.md @@ -6,8 +6,8 @@ You are a relay-connected worker in a coordinated multi-agent team. Your job is You MUST complete these steps in order before doing any work: -1. **Authenticate.** Your task prompt includes a workspace key. Call the `set_workspace_key` MCP tool with that key. Do not print the key to the user. -2. **Register with your assigned name.** Call the `register_agent` MCP tool with the agent name from your task prompt and `type: "agent"`. You must register before you can send or receive messages. +1. **Register with your assigned name.** Call the `register_agent` MCP tool with the agent name from your task prompt and `type: "agent"`. You must register before you can send or receive messages. The workspace is already pinned to this project, so the relay MCP server picks it up for you — you do not need a workspace key. If `register_agent` fails with "Workspace key not configured", report that to your lead instead of asking for the key; the lead fixes the pin. +2. **Never print or request a workspace key.** It is an administrative credential. If someone needs to watch this run, that is the lead's job via `get_observer_url`. 3. **Check your inbox.** Call `check_inbox` with your assigned relay name in `as` to find your task assignment and lead information. 4. **Send an ACK.** Before you do substantive work, send `ACK: ` to your lead via `send_dm`, again using your assigned relay name in `as`. 5. If the task is ambiguous or blocked, send `BLOCKED: ` instead of guessing. diff --git a/plugins/claude-relay-plugin/hooks/subagent-bootstrap.sh b/plugins/claude-relay-plugin/hooks/subagent-bootstrap.sh index 9e40eb4..29bfd90 100755 --- a/plugins/claude-relay-plugin/hooks/subagent-bootstrap.sh +++ b/plugins/claude-relay-plugin/hooks/subagent-bootstrap.sh @@ -7,13 +7,15 @@ AGENT_NAME="${RELAY_AGENT_NAME:-the assigned subagent name}" cat <`. Do not print a placeholder — print the real URL the user can click. This is mandatory. -4. Confirm the work is truly parallelizable. Every worker should be able to finish without waiting on another worker's output. -5. Decide the worker count from the task shape. Prefer 2 to 8 workers, but keep the count low enough that you can still monitor ACKs and completions reliably. -6. Partition the work into independent units. Each unit should have its own files, target, or scope boundary and should not require shared intermediate state. -7. Spawn one worker per unit using the Agent tool. **You must include the workspace key in the prompt**: +1. Pick a stable coordinator name — `relay-lead`. Pass `as: "relay-lead"` on **every** relay tool call you make, so your messages, inbox reads, and reactions stay attributed to the lead. +2. **Set up the workspace.** Call `register_agent` with `relay-lead`. If it fails with "Workspace key not configured", call `create_workspace`, then `register_agent` again. Both `create_workspace` and `set_workspace_key` pin the workspace to this project, which is how workers pick it up. +3. **Give the user a link to follow along.** Call `get_observer_url` and print the URL it returns. It is backed by a read-only token that expires, so it is safe to share. Never build an observer URL from the workspace key, and never print the key. +4. Confirm the work is genuinely parallelizable. Every worker must be able to finish without waiting on another worker's output. If that is not true, use the pipeline pattern instead. +5. Pick the worker count from the task shape. Prefer 2–8, and stay low enough that you can still track every ACK and DONE. +6. Partition the work into independent units — each with its own files, target, or scope boundary, and no shared intermediate state. +7. Spawn one worker per unit with the Agent tool: ``` Agent( subagent_type: "relay-worker", run_in_background: true, prompt: "You are relay-worker-N. Your lead is relay-lead. - Workspace key: . - CRITICAL: On every relay tool call, include as: \"relay-worker-N\". Without as, your messages can be attributed to another agent. + CRITICAL: pass as: \"relay-worker-N\" on every relay tool call, or your messages + can be attributed to another agent. Your unit: [specific target/scope]. Files: [list of files/directories]. - Deliver: [concrete output]." + Deliver: [concrete output]. + Do NOT release yourself when done — report DONE and stay idle for review." ) ``` -8. Each worker's prompt must include: - - the workspace key - - the unit it owns - - the exact files, directories, or target it should handle - - its assigned relay name and who its lead is - - a reminder to use `as: ""` on every relay tool call -9. Wait for ACK from every worker via relay inbox with `check_inbox(as: "relay-lead")`. Missing ACK means the worker is not ready. -10. Let workers run independently. Only send follow-up DMs for blockers, missing ACKs, or a global decision that changes all units, always using `as: "relay-lead"` for coordinator messages. -11. Collect all DONE messages, verify the outputs, and merge the final summary. Call out any units that finished partially or encountered blockers. +8. Wait for an ACK from every worker with `check_inbox(as: "relay-lead")`. A missing ACK means that worker is not working — re-DM it. +9. Keep a live worker table in your notes: name, unit, ACK, blocked, DONE. +10. Let workers run independently. Only DM them for blockers, missing ACKs, or a global decision that changes every unit. +11. Collect every DONE, verify the outputs yourself, and merge the summary. Call out units that finished partially or hit blockers. ## Rules - Do not use this pattern when stage N depends on stage N-1. That is a pipeline. -- Do not give multiple workers the same files unless duplicate review is intentional. -- Keep the task wording uniform so worker outputs are easy to compare and merge. -- Workers cannot spawn their own subagents — only the lead can spawn workers. +- Do not give two workers the same files unless duplicate review is the point. +- Keep the task wording uniform across units so the outputs are easy to compare and merge. +- Workers cannot spawn their own subagents — only the lead spawns. diff --git a/plugins/claude-relay-plugin/skills/relay-pipeline/SKILL.md b/plugins/claude-relay-plugin/skills/relay-pipeline/SKILL.md index dac0a0b..85df127 100644 --- a/plugins/claude-relay-plugin/skills/relay-pipeline/SKILL.md +++ b/plugins/claude-relay-plugin/skills/relay-pipeline/SKILL.md @@ -11,57 +11,55 @@ $ARGUMENTS ## How spawning works -Workers are spawned using Claude Code's built-in **Agent tool**, not the relay MCP tools. The relay is only used for communication between agents. +Workers are spawned with Claude Code's built-in **Agent tool**. The relay is only used for communication between agents. -- You **must** use `subagent_type: "relay-worker"` when spawning workers. Only `relay-worker` subagents get the Agent Relay MCP server, inbox-polling hooks, and the worker protocol. Regular subagent types (e.g. `researcher`, `general-purpose`) cannot communicate via relay. -- Run pipeline stages in **foreground mode** (default) so you wait for each stage to complete before starting the next. -- Each worker's prompt **must include the workspace key** so the worker can authenticate. See the spawn example below. -- The `SubagentStart` hook automatically injects relay bootstrap instructions into every spawned worker. -- Do not introduce extra setup scripts or dependencies in this workflow. Use the existing plugin hooks, Agent Relay MCP tools, and `relay-worker` agent definition only. -- Use relay MCP tools (`send_dm`, `check_inbox`) to receive handoff artifacts from each stage. +- Use `subagent_type: "relay-worker"`. Only `relay-worker` subagents get the Agent Relay MCP server, the inbox-polling hooks, and the worker protocol. Other subagent types (`researcher`, `general-purpose`, …) cannot talk over the relay. +- Run pipeline stages in **foreground mode** (the default) so each stage finishes before the next starts. +- Workers inherit the workspace automatically — the relay MCP server resolves the workspace pinned to this project. **Do not put the workspace key in a worker prompt.** It is an administrative credential, and copying it into N prompts puts it in N transcripts. If a worker reports no workspace, fix the pin (step 2) rather than pasting the key. +- The `SubagentStart` hook injects the relay bootstrap (register, check inbox, ACK, DONE) into every worker. +- Use the relay MCP tools (`send_dm`, `check_inbox`) to receive each stage's handoff. +- Do not add setup scripts or dependencies. Use the plugin's existing hooks, MCP tools, and `relay-worker` agent definition. ## Protocol -1. Pick a stable coordinator name such as `relay-lead`. On every relay tool call you make as the coordinator, include `as: "relay-lead"` so your messages, inbox checks, and reactions stay attributed to the lead. -2. **Set up the workspace.** Try calling `register_agent` with a coordinator name like `relay-lead`. If it fails with "Workspace key not configured", call `create_workspace` to generate one, then call `set_workspace_key` with the returned key, then `register_agent`. Save the workspace key — you will pass it to every worker. -3. **Tell the user they can follow along with the conversation.** Print the full observer URL with the real key value: `https://agentrelay.com/observer?key=`. Do not print a placeholder — print the real URL the user can click. This is mandatory. -4. Break the task into ordered stages. Each stage must have a clear handoff artifact for the next stage: a summary, decision, file path, diff, or verified output. -5. Keep the number of stages low and explicit. Prefer 2 to 5 stages with distinct responsibilities. -6. Start stage 1. Spawn its worker using the Agent tool in foreground mode. **Include the workspace key**: +1. Pick a stable coordinator name — `relay-lead`. Pass `as: "relay-lead"` on **every** relay tool call you make, so your messages, inbox reads, and reactions stay attributed to the lead. +2. **Set up the workspace.** Call `register_agent` with `relay-lead`. If it fails with "Workspace key not configured", call `create_workspace`, then `register_agent` again. Both `create_workspace` and `set_workspace_key` pin the workspace to this project, which is how workers pick it up. +3. **Give the user a link to follow along.** Call `get_observer_url` and print the URL it returns. It is backed by a read-only token that expires, so it is safe to share. Never build an observer URL from the workspace key, and never print the key. +4. Break the task into ordered stages. Every stage needs a concrete handoff artifact for the next one: a summary, a decision, a file path, a diff, or a verified output. +5. Keep the stage count low and explicit — prefer 2–5 with distinct responsibilities. +6. Start stage 1. Spawn its worker with the Agent tool in foreground mode: ``` Agent( subagent_type: "relay-worker", prompt: "You are relay-stage-1. Your lead is relay-lead. - Workspace key: . - CRITICAL: On every relay tool call, include as: \"relay-stage-1\". Without as, your messages can be attributed to another agent. + CRITICAL: pass as: \"relay-stage-1\" on every relay tool call, or your messages + can be attributed to another agent. Your task: [stage 1 scope]. Files: [relevant files]. - When done, send your lead a DONE message with: [handoff artifact description]." + When done, DM your lead a DONE message containing: [handoff artifact description]. + Do NOT release yourself when done — stay idle in case the stage needs a fix." ) ``` -7. Wait for the stage 1 DONE message via relay inbox with `check_inbox(as: "relay-lead")`. Do not start downstream work on assumptions. -8. For each later stage, spawn a new worker with: - - the workspace key - - the original task context - - the upstream DONE summary and handoff artifact - - any produced files, decisions, or constraints from previous stages +7. Wait for stage 1's DONE with `check_inbox(as: "relay-lead")`. Never start downstream work on an assumption about what the stage produced. +8. For each later stage, spawn a worker carrying the original task context, the upstream DONE summary and handoff artifact, and any files, decisions, or constraints the earlier stages produced: ``` Agent( subagent_type: "relay-worker", prompt: "You are relay-stage-2. Your lead is relay-lead. - Workspace key: . - CRITICAL: On every relay tool call, include as: \"relay-stage-2\". Without as, your messages can be attributed to another agent. + CRITICAL: pass as: \"relay-stage-2\" on every relay tool call. Previous stage completed: [DONE summary from stage 1]. - Your task: [stage 2 scope using stage 1 output]. - Files: [relevant files]." + Your task: [stage 2 scope, using stage 1's output]. + Files: [relevant files]. + Do NOT release yourself when done — stay idle in case the stage needs a fix." ) ``` -9. After each stage finishes, validate that the handoff is sufficient. If the output is ambiguous, ask the user for clarification before starting the next stage. -10. Continue until the final stage completes, then synthesize the end-to-end result and highlight where each handoff happened. +9. Keep a live stage table in your notes: stage, scope, ACK, blocked, DONE, handoff artifact. +10. After each stage, check the handoff is sufficient. If it is ambiguous, ask the user before starting the next stage. +11. When the last stage finishes, synthesize the end-to-end result and show where each handoff happened. ## Rules -- Use pipeline only for genuine dependencies. If stages can run independently, switch to fan-out. -- Handoffs must be explicit. A downstream worker should never need to guess what mattered from the previous stage. -- If a stage fails or is blocked, stop the pipeline, resolve the blocker, and then resume from the blocked stage. -- Workers cannot spawn their own subagents — only the lead can spawn workers. +- Use a pipeline only for genuine dependencies. If the stages can run independently, switch to fan-out. +- Handoffs must be explicit. A downstream worker should never have to guess what mattered upstream. +- If a stage fails or is blocked, stop the pipeline, resolve the blocker, and resume from that stage. +- Workers cannot spawn their own subagents — only the lead spawns. diff --git a/plugins/claude-relay-plugin/skills/relay-team/SKILL.md b/plugins/claude-relay-plugin/skills/relay-team/SKILL.md index a0c4691..a3d86ce 100644 --- a/plugins/claude-relay-plugin/skills/relay-team/SKILL.md +++ b/plugins/claude-relay-plugin/skills/relay-team/SKILL.md @@ -11,45 +11,46 @@ $ARGUMENTS ## How spawning works -Workers are spawned using Claude Code's built-in **Agent tool**, not the relay MCP tools. The relay is only used for communication between agents. +Workers are spawned with Claude Code's built-in **Agent tool**. The relay is only used for communication between agents. -- You **must** use `subagent_type: "relay-worker"` when spawning workers. Only `relay-worker` subagents get the Agent Relay MCP server, inbox-polling hooks, and the worker protocol. Regular subagent types (e.g. `researcher`, `general-purpose`) cannot communicate via relay. -- Run workers in **background mode** (`run_in_background: true`) so they execute concurrently. -- Each worker's prompt **must include the workspace key** so the worker can authenticate. See the spawn example below. -- The `SubagentStart` hook automatically injects relay bootstrap instructions (register, check inbox, ACK, DONE protocol) into every spawned worker. -- Do not introduce extra setup scripts or dependencies in this workflow. Use the existing plugin hooks, Agent Relay MCP tools, and `relay-worker` agent definition only. -- Use relay MCP tools (`send_dm`, `post_message`, `check_inbox`) to communicate with workers after they're running. +- Use `subagent_type: "relay-worker"`. Only `relay-worker` subagents get the Agent Relay MCP server, the inbox-polling hooks, and the worker protocol. Other subagent types (`researcher`, `general-purpose`, …) cannot talk over the relay. +- Run workers in **background mode** (`run_in_background: true`) so they work concurrently. +- Workers inherit the workspace automatically — the relay MCP server resolves the workspace pinned to this project. **Do not put the workspace key in a worker prompt.** It is an administrative credential, and copying it into N prompts puts it in N transcripts. If a worker reports no workspace, fix the pin (step 2) rather than pasting the key. +- The `SubagentStart` hook injects the relay bootstrap (register, check inbox, ACK, DONE) into every worker. +- Use the relay MCP tools (`send_dm`, `post_message`, `check_inbox`) to talk to workers once they are running. +- Do not add setup scripts or dependencies. Use the plugin's existing hooks, MCP tools, and `relay-worker` agent definition. ## Protocol -1. Pick a stable coordinator name such as `relay-lead`. On every relay tool call you make as the coordinator, include `as: "relay-lead"` so your messages, inbox checks, and reactions stay attributed to the lead. -2. **Set up the workspace.** Try calling `register_agent` with a coordinator name like `relay-lead`. If it fails with "Workspace key not configured", call `create_workspace` to generate one, then call `set_workspace_key` with the returned key, then `register_agent`. Save the workspace key — you will pass it to every worker. -3. **Tell the user they can follow along with the conversation.** Print the full observer URL with the real key value: `https://agentrelay.com/observer?key=`. Do not print a placeholder — print the real URL the user can click. This is mandatory. -4. Read the task, inspect the relevant code or files, and decide whether parallel work is justified. Prefer 1 worker for tightly coupled work and 2 to 5 workers for genuinely separable work. -5. Break the task into clear, non-overlapping worker scopes. Each worker needs a concrete deliverable, the relevant files or directories, and an explicit success condition. -6. Spawn each worker using the Agent tool. **You must include the workspace key in the prompt** so the worker can call `set_workspace_key`: +1. Pick a stable coordinator name — `relay-lead`. Pass `as: "relay-lead"` on **every** relay tool call you make, so your messages, inbox reads, and reactions stay attributed to the lead. +2. **Set up the workspace.** Call `register_agent` with `relay-lead`. If it fails with "Workspace key not configured", call `create_workspace`, then `register_agent` again. Both `create_workspace` and `set_workspace_key` pin the workspace to this project, which is how workers pick it up. +3. **Give the user a link to follow along.** Call `get_observer_url` and print the URL it returns. It is backed by a read-only token that expires, so it is safe to share. Never build an observer URL from the workspace key, and never print the key. +4. Read the task, inspect the relevant code, and decide whether parallel work is justified. Prefer 1 worker for tightly coupled work, 2–5 for genuinely separable work. +5. Break the task into non-overlapping scopes. Each worker needs a concrete deliverable, the relevant files, and an explicit success condition. +6. Spawn each worker with the Agent tool: ``` Agent( subagent_type: "relay-worker", run_in_background: true, prompt: "You are relay-worker-1. Your lead is relay-lead. - Workspace key: . - CRITICAL: On every relay tool call, include as: \"relay-worker-1\". Without as, your messages can be attributed to another agent. + CRITICAL: pass as: \"relay-worker-1\" on every relay tool call, or your messages + can be attributed to another agent. Your task: [specific scope and deliverables]. Files: [list of files/directories]. - Success condition: [what done looks like]." + Success condition: [what done looks like]. + Do NOT release yourself when done — report DONE and stay idle for review." ) ``` -7. After spawning, send each worker a DM via relay with any additional context they need. Include `as: "relay-lead"` on those coordinator messages. -8. Monitor the relay inbox for ACKs with `check_inbox(as: "relay-lead")`. Do not assume a worker is active until it ACKs. Send a follow-up DM if an ACK does not arrive. -9. Maintain a live worker table in your own notes with: worker name, scope, ACK status, blocker status, and DONE status. -10. Coordinate dependencies explicitly. Relay only the minimum context each worker needs, and keep workers independent whenever possible. -11. Collect every DONE message, verify the results, and synthesize the final output. Include what each worker finished and any remaining gaps or risks. +7. After spawning, DM each worker any extra context it needs. +8. Watch for ACKs with `check_inbox(as: "relay-lead")`. A worker is not working until it ACKs — expect a cold-start delay, and re-DM if an ACK never arrives. +9. Keep a live worker table in your notes: name, scope, ACK, blocked, DONE. +10. Coordinate dependencies explicitly. Send each worker the minimum context it needs and keep workers independent where you can. +11. Collect every DONE, verify the results yourself, and synthesize the final answer — what each worker finished, plus remaining gaps and risks. ## Rules -- Prefer fewer well-scoped workers over many vague workers. -- Do not let workers infer coordination details. Send explicit follow-up instructions when assumptions change. -- If the task turns out to be independent across targets, switch to the fan-out pattern instead of keeping a central coordinator busy. -- If the task turns out to be sequential, switch to the pipeline pattern instead of forcing parallelism. -- Workers cannot spawn their own subagents — only the lead can spawn workers. +- Prefer fewer well-scoped workers over many vague ones. +- Do not let workers infer coordination details. Send explicit follow-ups when assumptions change. +- If the work turns out to be independent across targets, switch to fan-out instead of keeping a coordinator busy. +- If it turns out to be sequential, switch to pipeline instead of forcing parallelism. +- Workers cannot spawn their own subagents — only the lead spawns.