feat: add Codex relay skill for sub-agent communication#595
feat: add Codex relay skill for sub-agent communication#595khaliqgant merged 18 commits intomainfrom
Conversation
Add a Codex-native skill that enables peer-to-peer sub-agent communication via Relaycast MCP, filling the gap in Codex's parent-orchestrated sub-agent model. Includes: - SKILL.md with relay protocol (ACK/STATUS/BLOCKED/DONE) - Lifecycle hooks: SessionStart, Stop, UserPromptSubmit - relay-worker.toml custom agent definition - MCP server config template for .codex/config.toml - agents/openai.yaml MCP dependency declaration Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Include features.codex_hooks = true and hooks.json installation in the setup instructions. Without these, the lifecycle hooks (auto-connect, inbox polling, stop guard) don't fire. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Installation is now a single cp command. The skill self-installs on first activation via scripts/setup.sh — no manual config editing needed. Manual setup moved to a collapsible details section. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| if (!in_block && $0 ~ /^[[:space:]]*mcp_servers[.]relaycast[.]command[[:space:]]*=/) { | ||
| block_seen = 1 | ||
| dotted_seen = 1 | ||
| command_seen = 1 | ||
| command_line = $0 | ||
| sub(/^[[:space:]]*mcp_servers[.]relaycast[.]command[[:space:]]*=/, "command =", command_line) | ||
| next | ||
| } | ||
| if (!in_block && $0 ~ /^[[:space:]]*mcp_servers[.]relaycast[.]args[[:space:]]*=/) { | ||
| block_seen = 1 | ||
| dotted_seen = 1 | ||
| args_seen = 1 | ||
| args_line = $0 | ||
| sub(/^[[:space:]]*mcp_servers[.]relaycast[.]args[[:space:]]*=/, "args =", args_line) | ||
| next | ||
| } | ||
| if (!in_block && $0 ~ /^[[:space:]]*mcp_servers[.]relaycast[.]env[[:space:]]*=/) { | ||
| block_seen = 1 | ||
| dotted_seen = 1 | ||
| env_seen = 1 | ||
| env_line = $0 | ||
| sub(/^[[:space:]]*mcp_servers[.]relaycast[.]env[[:space:]]*=/, "env =", env_line) | ||
| next | ||
| } |
There was a problem hiding this comment.
🟡 AWK config migration silently drops dotted key values when a section block also exists
ensure_relaycast_mcp_block in setup.sh shares command_seen/args_seen/env_seen flags across both dotted-key processing (lines 129-152) and section-block processing (lines 168-183). When a config file has both a dotted key like mcp_servers.relaycast.command = "custom" and a [mcp_servers.relaycast] section, the dotted key line is consumed via next (not printed) and its _seen flag is set. Later, when the section block is processed, write_missing_keys() skips that key because _seen is already true. The net result is the dotted key's value is silently dropped from the output — e.g. the command line disappears entirely, producing an invalid MCP server config without a command key.
Example input that triggers the bug
mcp_servers.relaycast.command = "custom-cmd"
[mcp_servers.relaycast]
args = ["-y", "@relaycast/mcp"]
env = { RELAY_API_KEY = "" }Output is missing command:
[mcp_servers.relaycast]
args = ["-y", "@relaycast/mcp"]
env = { RELAY_API_KEY = "" }Prompt for agents
In plugins/codex-relay-skill/scripts/setup.sh, the ensure_relaycast_mcp_block AWK script (lines 102-210) uses shared _seen flags for both dotted-key and section-block processing. When dotted keys are consumed (lines 129-152, via next), their _seen flags prevent write_missing_keys() from emitting those values later.
Fix: When a [mcp_servers.relaycast] section header is encountered and dotted_seen is true (line 158), reset the _seen flags back to 0 and re-set command_line/args_line/env_line from the stored dotted values so that write_missing_keys() writes any keys not already present in the section block. Alternatively, when in_block is entered and command_seen was set by dotted processing, print command_line immediately and only then set command_seen for the section scope.
Was this helpful? React with 👍 or 👎 to provide feedback.
| write_file "$KEY_FILE" "$workspace_key" | ||
| write_file "$TOKEN_FILE" "$token" |
There was a problem hiding this comment.
🔴 Token/state file path mismatch: session-start.sh saves to re-namespaced dir but prompt-inbox.sh and stop-inbox.sh look in the original dir
When RELAY_AGENT_NAME is not set (the default per the README), session-start.sh re-namespaces RELAY_DIR to $HOME/.relay/agents/<derived-name> at line 205 after calling derive_agent_name(), and persists the token and state there (line 235 → persist_state). However, prompt-inbox.sh and stop-inbox.sh both compute RELAY_DIR solely from RELAY_AGENT_NAME at startup (prompt-inbox.sh:11-16, stop-inbox.sh:11-16). When RELAY_AGENT_NAME is empty, they set RELAY_DIR=$HOME/.relay and look for the token at $HOME/.relay/token — but the token was actually saved at $HOME/.relay/agents/<derived-name>/token. Since read_token() finds no file and no RELAY_TOKEN env var, both hooks silently exit with {}, effectively disabling inbox polling and the stop guard in the default configuration.
Concrete path trace for default case (RELAY_AGENT_NAME unset)
session-start.shline 14-15:RELAY_DIR = $HOME/.relay(empty namespace)session-start.shline 202:agent_name = codex-user-host-123456(derived)session-start.shline 205:RELAY_DIR = $HOME/.relay/agents/codex-user-host-123456(re-namespaced)session-start.shline 145: token written to$HOME/.relay/agents/codex-user-host-123456/tokenprompt-inbox.shline 14-15:RELAY_DIR = $HOME/.relay(empty namespace, no re-namespace step)prompt-inbox.shline 17:TOKEN_FILE = $HOME/.relay/token→ file not found → hook silently no-ops- Same for
stop-inbox.sh
Was this helpful? React with 👍 or 👎 to provide feedback.
Add step in SKILL.md startup protocol to show the user the agentrelay.dev/observer URL so they can follow agent conversations live. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The startup protocol now explicitly tells Codex to check for a workspace, create one if missing, and register — using actual MCP tool names. Previously relied on hooks that may not fire on first session. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address four issues from real-world testing: 1. Explicit workspace bootstrap — never assume one exists 2. Distinguish Relaycast agents (agent.add) from Codex sub-agents (spawn_agent) — use the right tool for the job 3. Add deterministic ACK fallback sequence (30s timeout) 4. Tighten handoff templates with exact tool call sequences Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
c485aab to
8e83993
Compare
- Add chmod 600 after writing credential files (workspace-key, token, session state) in session-start.sh to prevent world-readable secrets - Fix AWK config migration in setup.sh that silently dropped dotted-key values when both dotted keys and a [mcp_servers.relaycast] section block existed in the same TOML file - Add ensure_top_level_approval_policy() to write approval_policy at the TOML top level instead of accidentally scoping it under [features] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…r agent hooks.json referenced plugins/codex-relay-skill/hooks/ but the README instructs users to install to .agents/skills/agent-relay/. Updated all paths to match the documented install location. All scripts now namespace state files under ~/.relay/agents/<name>/ using RELAY_AGENT_NAME env var (or the derived agent name in session-start.sh) so concurrent agents no longer overwrite each other's token, session, and poll state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…intent) The file was removed on this branch as part of moving plugins to the skills repo. Main had a tweak to the same file (#594). Keeping it deleted per this branch's intent. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ctive mode `opencode --prompt` launches the interactive TUI, which renders escape codes to stdout and hangs when there's no TTY. The correct headless command is `opencode run <message>`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
plugins/codex-relay-skill/) that enables peer-to-peer sub-agent communication via Relaycast MCPWhat's included
SKILL.mdhooks/hooks.jsonhooks/session-start.shhooks/stop-inbox.shhooks/prompt-inbox.shagents/openai.yamlcodex-config/config.toml.codex/config.tomlcodex-config/relay-worker.toml.codex/agents/README.mdHook mapping (Claude → Codex)
PostToolUseUserPromptSubmitStopStopdecision: "block"SessionStartSessionStartSubagentStartPreCompactTest plan
.agents/skills/agent-relay/and verify Codex discovers itsession-start.shcreates workspace and persists state to~/.relay/stop-inbox.shblocks exit when unread messages existprompt-inbox.shinjects inbox messages before prompts.codex/config.toml🤖 Generated with Claude Code