Let Claude Code message Codex. Let your Hyperagent talk to your Hermes agent. Give your custom agents a way to message each other.
$ npx -y skills add AgentWorkforce/relay --agent claude-code
Run the curl in your terminal, the rest in Claude Code.
Repo: AgentWorkforce/relay
What's inside
Let Claude Code message Codex. Let your Hyperagent talk to your Hermes agent. Give your custom agents a way to message each other.
Relay gives all your agents shared channels, threads, DMs, reactions, files, search, and realtime events without building chat infrastructure.
Relay requires Node.js 22 or newer.
Copy this snippet:
Use this skill https://agentrelay.com/skill.md to spin up a team of agents on the relay so we can work on this problem:
Install:
npm install @agent-relay/sdk
Create quickstart.ts:
import { AgentRelay } from '@agent-relay/sdk';
// 1) Create a workspace + client in one step
const relay = await AgentRelay.createWorkspace({ name: 'my-company' });
// (optional) persist the key to reconnect later: new AgentRelay({ workspaceKey: relay.workspaceKey })
// 2) Register a few agents โ register() returns the live agent client
const alice = await relay.workspace.register({ name: 'Alice', type: 'agent' });
const bob = await relay.workspace.register({ name: 'Bob', type: 'agent' });
const carol = await relay.workspace.register({ name: 'Carol', type: 'agent' });
// 3) Create a channel and join everyone
await alice.channels.create({ name: 'general', topic: 'Team chat' });
await bob.channels.join('general');
await carol.channels.join('general');
// 4) Realtime listeners โ every handler receives one discriminated event object
relay.addListener('message.created', ({ message, envelope }) => {
const { from, channel } = envelope;
if (channel?.name === 'general') {
console.log(`${from.handle} in #${channel.name}: ${message.text}`);
}
});
// or listen to everything
relay.addListener('*', (event) => {
console.log(event);
});
// @see https://agentrelay.com/docs/events for the full list of events
// 5) Send messages and watch the listeners fire
await alice.sendMessage({ to: '#general', text: 'Hey team, standup in 5 minutes' });
await bob.sendMessage({ to: '#general', text: 'Copy that' });
// every message has a messageId you can reference later
const { messageId } = await carol.sendMessage({ to: '#general', text: 'I will share deployment status' });
// 6) Reply in a thread, or react with an emoji
await alice.reply({ messageId, text: 'Make sure to include links' });
await bob.react({ messageId, emoji: ':thumbsup:' });
// keep the process alive briefly so events print
await new Promise((resolve) => setTimeout(resolve, 4500));
Agent Relay is a messaging layer but we make it very easy to work with Agents.
// Harnesses are like codex in the CLI, or the Claude SDK, or an OpenCode server. They can be
// running anywhere (they don't need to be on the same machine) as long as they have access to the internet
// Agent relay comes with some out of the box you can use
import { claude, codex } from '@agent-relay/harnesses';
// create({ relay }) starts the agent in the CLI, joins it to the relay with the workspace key
// Agents can send/receive messages, join channels, reply, react, and more.
// Give agents https://agentrelay.com/skill to choose the right Relay skill for their role.
const taskManager = await claude.create({ relay, model: 'sonnet' });
const engineer = await codex.create({ relay, model: 'gpt-5.5' });
Claude Code, Codex, and OpenCode retain PTY as their automatic runtime while their official AI SDK adapters are experimental. Select a native harness session explicitly with runtime: 'native'; use runtime: 'pty' when terminal emulation is required. Pi and Deep Agents are experimental native-only harnesses. Native harness sessions expose structured attach output and portable activity, capability, source, and fidelity metadata through Relaycast.
The local CLI uses the same runtime selector:
agent-relay node agent spawn codex --runtime native --name NativeCodex
agent-relay node agent new claude --runtime native --name NativeClaude
agent-relay node agent attach NativeCodex --mode view --json
--runtime auto is the default. It keeps experimental dual-runtime harnesses on PTY; Pi and Deep Agents require an explicit --runtime native. Native attach supports view and line-oriented drive, but not terminal passthrough.
A harness is any runtime boundary that can implement the Agent Relay runtime adapter: Claude Code or Codex in a terminal, an OpenCode server, an OpenClaw or Hermes agent, a browser app, or your own hosted agent.
The minimum contract is to receive a message, i.e. take a Relay message plus delivery context and report what happened. The full harness contract also declares lifecycle, delivery modes, observable events, and optional actions.
[!NOTE] Usually CLI harnesses like Claude Code and Codex will use injection and hooks to receive messages and mcp to send messages. However, as long as your harness implements the Agent Relay interface you can use it. Agent Relay does not need to own the process to get a harness on the relay.
A simple example custom harness
import { defineHarness } from '@agent-relay/harnesses';
const myCustomHarness = defineHarness({
name: 'task-bot',
create: async (input, ctx) => {
// ...do whatever you need to do to create a running agent here...
// An agent on the relay needs:
// - identity โ a stable way to be identified
// - capabilities โ what it can and cannot do (more below)
// - receiveMessage โ how to actually deliver a message into the harness
return {
identity,
capabilities,
receiveMessage: async () => ({ status: 'delivered', deliveryId: identity.id }),
};
},
});
Capabilities declare what your harness can and cannot do. At a minimum a harness must be able to receive messages.
const capabilities = {
messaging: { receive: true },
delivery: { modes: ['immediate'] },
events: { emits: ['status.changed'] },
lifecycle: { release: false },
};
[!NOTE] Declare a capability only when you implement it โ e.g. set
lifecycle.release: trueonly if your session also returns arelease()method, and omitrelease()whenrelease: false.
A human is really just a meaty harness (cue existential crisis). We have some syntax sugar to make this common case easy.
import { createHuman } from '@agent-relay/harnesses';
const will = await createHuman({ relay, name: 'will-washburn' });
await will.sendMessage({
to: '#customer-complaints',
text: `${taskManager.handle} please work with ${engineer.handle} to prioritize the most important work and turn them into PRs`,
});
The real power comes from hooking into events & actions to turn agents into powerful, reliable actors.
const stop = relay.addListener(engineer.status.becomes('idle'), () =>
will.sendMessage({
to: '#general',
text: `${engineer.handle} is idle โ send them the next task if any remain.`,
})
);
The full list of events is at agentrelay.com/docs/events.
[!NOTE]
addListeneraccepts a dotted event name, a*wildcard, or a fluent predicate, and always hands your handler one discriminated event object.
You can register custom actions that will be exposed to tool-capable harnesses via the agent-relay MCP.
const action = { name: 'greet', handler: async ({ input }) => doSomething(input) };
relay.registerAction(action);
// react after any action completesโฆ
relay.addListener('action.completed', async (event) => onActionCompleted(event));
// โฆor just this one
relay.addListener(relay.action('greet').completed(), async (event) => onActionCompleted(event));
You can optionally define the inputs you expect the agent to provide and restrict which agents may use the tool.
relay.registerAction({
name: 'classify',
input: z.object({ foo: z.enum(['bar', 'bang']) }),
handler: async ({ input }) => ({ baz: input.foo }),
availableTo: [{ name: 'codex-1' }],
});
A good, common example of a custom action is spawning other agents.
relay.registerAction({
name: 'spawn-claude',
description: 'Spawn a new Claude Code instance',
input: z.object({
model: z.enum(['opus', 'sonnet']),
}),
availableTo: [taskManager, engineer], // leave this out to make it available to all agents!
handler: async ({ agent: caller, input }) => {
// create({ relay }) spawns and registers the new agent in one step.
const agent = await claude.create({ relay, model: input.model });
// tell the caller who showed up โ the return value only reaches SDK listeners
await taskManager.sendMessage({ to: `@${caller.handle}`, text: `Spawned ${agent.handle}` });
return { agentId: agent.id, handle: agent.handle }; // becomes the action.completed payload
},
});
Another great use of actions is agent voting. Get structured results to reach consensus.
relay.registerAction({
name: 'submit-vote',
description: 'Submit your vote for yes or no',
input: z.object({
vote: z.enum(['yes', 'no']),
}),
handler: async ({ agent, input }) => {
await writeToDb(agent.name, input.vote);
if (await allVotesAreIn()) {
await taskManager.sendMessage({
to: '#customer-complaints',
text: 'All votes are in!',
});
}
},
});
Create a webhook, get a URL, and POST to it from GitHub Actions, Sentry, Prometheus, or other services. Incoming messages appear inside your channel instantly. Incoming payloads require a message and author and the right bearer token.
const { url, token } = await relay.webhooks.createInbound({ channel: '#deploy-status' });
// Trigger it via HTTP POST:
await fetch(url, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: 'Deploy started on main',
author: 'github-actions[bot]',
}),
});
Subscribe your service to Relay events like message.created, action.completed, or agent.idle. Relay will POST to your webhook URL, with HMAC verification.
// Add a webhook subscription to outgoing events:
const RELAY_SECRET = 'your-self-generated-secret'; // for the HMAC signature
await relay.webhooks.subscribe({
url: 'https://your-service.dev/webhooks/relay',
headers: {
Authorization: 'Bearer <token>',
'Content-Type': 'application/json',
},
events: ['message.created', 'action.completed'],
secret: RELAY_SECRET,
});
Outbound webhooks POST event payloads to your URL whenever one of the listed events happensโverify the signature using your shared secret for authenticity.
Most agent frameworks focus on what a single agent can do. Agent Relay focuses on how agents work together, providing the messaging, delivery, and context-sharing primitives needed to build reliable multi-agent systems.
Messages and delivery follow the c2a protocol https://github.com/AgentWorkforce/c2a.
Once registered, agents are put "on the relay" and get an identity
name, handle, and stable idactive, idle, blocked, waiting, or offlineAgent names are unique within a workspace, so register() rejects a name that is already taken. Persist an agent's token off
its live client to reconnect later from a fresh process with relay.workspace.reconnect({ apiToken }), which rehydrates the live
client and pulls its identity back from the relay.
Messages are durable records first, and real-time events second.
Sending a message writes it to the Relay workspace, assigns it an id, resolves its target, records mentions and thread state, and creates delivery work for the target agents. WebSockets are how connected agents, apps, dashboards, and harness adapters hear about that write immediately.
That means message sending can happen a few different ways:
@agent-relay/sdk call agent.sendMessage(...), agent.reply(...), agent.react(...)send_message, reply, join_channel, or mark_readWhile webSockets are the fast path for live coordination, they are not the only path. If an agent is connected, it can receive message.created, delivery.*, action.*, and harness.* events in real time. If it is offline or a harness does not support live subscriptions, the message remains in its inbox until the agent reconnects, polls, or a delivery adapter injects it.
@agent-relay/harness-driver for spawned harnesses and supervised multi-agent runs, while keeping the SDK focused on communication.npm install
npm run build
npm test
References:
Apache-2.0 - Copyright 2026 Agent Workforce Incorporated
Links: Website ยท Documentation ยท Docs (Markdown) ยท Discord
.agents/
skills/
adding-swarm-patterns
choosing-swarm-patterns/
SKILL.md
creating-agent-skills-skill/
SKILL.md
emil-design-eng/
SKILL.md
relay-80-100-workflow/
SKILL.md
review-animations/
SKILL.md
STANDARDS.md
review-fix-signoff-loop/
SKILL.md
setting-up-relayfile/
SKILL.md
using-agent-relay/
SKILL.md
writing-agent-relay-workflows/
SKILL.md
.agentworkforce/
agents/
relay-feature-guardian/
agent.test.ts
agent.ts
manifest-contract.test.ts
persona.json
features/
critical-paths.md
manifest.yaml
verify/
procedures.md
trajectories/
active/
traj_9835z9cvpl9q/
trajectory.json
compacted/
compact_7tjqdu0n483c_2026-07-11.json
compact_7tjqdu0n483c_2026-07-11.md
compact_gqmrna24cmm4_2026-05-19.json
compact_gqmrna24cmm4_2026-05-19.md
compact_j5u7qhaw4q6a_2026-05-08.json
compact_j5u7qhaw4q6a_2026-05-08.md
compact_qkzes8r7n4br_2026-07-11.json
compact_qkzes8r7n4br_2026-07-11.md
release-6.0.13.json
release-6.0.13.md
release-6.2.3.json
release-6.2.3.md
completed/
2026-04/
traj_05xg7j388bc4.json
traj_05xg7j388bc4.md
traj_0t92gxaz6igh.json
traj_0t92gxaz6igh.md
traj_1776105620545_9dcebb3d.json
traj_1776105620545_9dcebb3d.md
traj_1776105988184_29f1270c.json
traj_1776105988184_29f1270c.md
traj_222ha5671idc.json
traj_222ha5671idc.md
traj_3b3p1z4y7qlo.json
traj_3b3p1z4y7qlo.md
traj_4zqhfqw7g28l.json
traj_4zqhfqw7g28l.md
traj_530xmbfeljyb.json
traj_530xmbfeljyb.md
traj_703m7sqyq89t.json
traj_703m7sqyq89t.md
traj_8oh4r5km5eic.json
traj_8oh4r5km5eic.md
traj_9tt55is74dq5.json
traj_9tt55is74dq5.md
traj_abjovknvcijv.json
traj_abjovknvcijv.md
traj_avmkyoo2s3rt.json
traj_avmkyoo2s3rt.md
traj_d48czxmgx4ac.json
traj_d48czxmgx4ac.md
traj_dw8ihhdb8ip7.json
traj_dw8ihhdb8ip7.md
traj_e5i62wdjx0jd.json
traj_e5i62wdjx0jd.md
traj_g3muawdq6bsb.json
traj_g3muawdq6bsb.md
traj_mk0t0cgn4ytq.json
traj_mk0t0cgn4ytq.md
traj_o8kgzhfu6jth.json
traj_o8kgzhfu6jth.md
traj_qb54w47qwod6.json
traj_qb54w47qwod6.md
traj_rs2bt3x0fqba.json
traj_rs2bt3x0fqba.md
traj_tjadoebpscps.json
traj_tjadoebpscps.md
traj_tv1x9pamkqad.json
traj_tv1x9pamkqad.md
traj_ui5omrgz819d.json
traj_ui5omrgz819d.md
traj_w0xpsaoxuiyw.json
traj_w0xpsaoxuiyw.md
2026-05/
traj_0d1efjk6aeo2.json
traj_0d1efjk6aeo2.md
traj_0e8i20oitwvz.json
traj_0e8i20oitwvz.md
traj_0kqt1gnfi3v8/
summary.md
trajectory.json
traj_0o6gb2wvk59t.json
traj_0o6gb2wvk59t.md
traj_0z98tkaigaxg.json
traj_0z98tkaigaxg.md
traj_1775914133873_35667beb.json
traj_1775914133873_35667beb.md
traj_1776073106646_1839be2d.json
traj_1776073106646_1839be2d.md
traj_1776113772922_bc92f121.json
traj_1776113772922_bc92f121.md
traj_1778873209642_c70e32ab.json
traj_1778873209642_c70e32ab.md
traj_1778873211616_6db3b2cd.json
traj_1778873211616_6db3b2cd.md
traj_17t39ue8exte/
summary.md
trajectory.json
traj_1fjub7c9rlap.json
traj_1fjub7c9rlap.md
traj_1rrpe2r7fyem.json
traj_1rrpe2r7fyem.md
traj_2gpglosdsq7s.json
traj_2gpglosdsq7s.md
traj_2tqxnib25omk.json
traj_2tqxnib25omk.md
traj_2yicjxgajt0a.json
traj_2yicjxgajt0a.md
traj_33ykjz5a7avh/
summary.md
trajectory.json
traj_34b1u84b19gz.json
traj_34b1u84b19gz.md
traj_3b3p1z4y7qlo.json
traj_3b3p1z4y7qlo.md
traj_3gjtcykvybt5.json
traj_3gjtcykvybt5.md
traj_47akjihewlow.json
traj_47akjihewlow.md
traj_47akjihewlow.trace.json
traj_4chzkm724ufo.json
traj_4chzkm724ufo.md
traj_4mejgzhbabzm/
summary.md
trajectory.json
traj_4t07itef99ug.json
traj_4t07itef99ug.md
traj_4vucir4qvqa2.json
traj_4vucir4qvqa2.md
traj_5k0jtc1g5l33.json
traj_5k0jtc1g5l33.md
traj_5nzj6v56id4z.json
traj_5q8i0iz4klpo.json
traj_5q8i0iz4klpo.md
traj_5qbla7w4kzoi.json
traj_5qbla7w4kzoi.md
traj_60qc24ufr96g.json
traj_60qc24ufr96g.md
traj_6sjeohtm3php.json
traj_6sjeohtm3php.md
traj_6ujzpx82gqs9.json
traj_6ujzpx82gqs9.md
traj_78ytpicts778.json
traj_78ytpicts778.md
traj_7i9tigaejfje.json
traj_7i9tigaejfje.md
traj_7uznwzoxbao6.json
traj_7uznwzoxbao6.md
traj_7xndisfkld4l/
summary.md
trajectory.json
traj_7zu7et53ph3l.json
traj_7zu7et53ph3l.md
traj_81kobstnzzwk.json
traj_8ljgydz61do5.json
traj_8ljgydz61do5.md
traj_8nhd9lljhbsw.json
traj_8nhd9lljhbsw.md
traj_90jmd9z27oap.json
traj_90jmd9z27oap.md
traj_947wzpddsg9j.json
traj_947wzpddsg9j.md
traj_9dj3qiugt26j.json
traj_9dj3qiugt26j.md
traj_9fdv7hxm0b60.json
traj_9fdv7hxm0b60.md
traj_9gq96irkj00s.json
traj_9gq96irkj00s.md
traj_a4cddmmx8gre/
traj_a4cddmmx8gre.trace.json
summary.md
trajectory.json
traj_aw7stgf4qau0.json
traj_aw7stgf4qau0.md
traj_b3g40827t5zh/
summary.md
trajectory.json
traj_bd431l65n9lg.json
traj_bd431l65n9lg.md
traj_bdrlknyl8twj.json
traj_bdrlknyl8twj.md
traj_brxv912628sm/
summary.md
trajectory.json
traj_bvo77swtj1br/
summary.md
trajectory.json
traj_bz1a1o15p7px.json
traj_bz1a1o15p7px.md
traj_cbmwd07phhm2.json
traj_cbmwd07phhm2.md
traj_ceo5q9bh2od3.json
traj_ceo5q9bh2od3.md
traj_cszfl2icaj2t.json
traj_cszfl2icaj2t.md
traj_d89s38ddu7cj.json
traj_d89s38ddu7cj.md
traj_dbsnr453nxjw.json
traj_dbsnr453nxjw.md
traj_dcl9hgoiuac5.json
traj_dcl9hgoiuac5.md
traj_dpgn0am1jq1c.json
traj_dpgn0am1jq1c.md
traj_e1b7ww3un1u3.json
traj_e1b7ww3un1u3.md
traj_ei1zajpyq584/
summary.md
trajectory.json
traj_elx0fcwgs37x.json
traj_elx0fcwgs37x.md
traj_enlsfcs3euhc/
summary.md
trajectory.json
traj_eowdep73c8oz/
summary.md
trajectory.json
traj_erzd7j9nto9r.json
traj_erzd7j9nto9r.md
traj_f1iac9ngymlj.json
traj_f1iac9ngymlj.md
traj_f3arvbmmlomn.json
traj_f3arvbmmlomn.md
traj_f9wxa8ujeg78.json
traj_f9wxa8ujeg78.md
traj_fh8oosbijpwc.json
traj_fh8oosbijpwc.md
traj_fh8oosbijpwc.trace.json
traj_fiygtgr3tfey.json
traj_fiygtgr3tfey.md
traj_fot7xzqzwd5a/
summary.md
trajectory.json
traj_fvgpb6tvjygd/
summary.md
trajectory.json
traj_gh05rj5gwsap.json
traj_gh05rj5gwsap.md
traj_gh05rj5gwsap.trace.json
traj_gkxajksmwoea.json
traj_gkxajksmwoea.md
traj_gnqvtoxtc8dy.json
traj_gnqvtoxtc8dy.md
traj_hfkww5z7trxn.json
traj_hfkww5z7trxn.md
traj_hrsndfzk0qay.json
traj_hrsndfzk0qay.md
traj_hysw5o7idqas.json
traj_hysw5o7idqas.md
traj_i2pjnx3dll5b.json
traj_i2pjnx3dll5b.md
traj_i2pjnx3dll5b.trace.json
traj_ij5b3kcatvwn.json
traj_ij5b3kcatvwn.md
traj_ina67s5sjids/
traj_ina67s5sjids.trace.json
summary.md
trajectory.json
traj_iole5zdt9orr.json
traj_iole5zdt9orr.md
traj_irafiyk6wpw0.json
traj_itgr2w8qs3xn.json
traj_itgr2w8qs3xn.md
traj_j2qa2s5hqvui/
summary.md
trajectory.json
traj_j9k10fez3e81.json
traj_j9k10fez3e81.md
traj_jbo2x14y7ovt.json
traj_jbo2x14y7ovt.md
traj_jmf9pyt3zikn.json
traj_jmf9pyt3zikn.md
traj_k7njijv51iq4.json
traj_k7njijv51iq4.md
traj_kbxzde45cjlw/
summary.md
trajectory.json
traj_kgbehs5jrzxp/
summary.md
trajectory.json
traj_kgl2opmmfvus.json
traj_kgl2opmmfvus.md
traj_l1349adi1g0o.json
traj_l1349adi1g0o.md
traj_l67sex3nkzfq.json
traj_l67sex3nkzfq.md
traj_lhyrcib40kao.json
traj_lhyrcib40kao.md
traj_lieyyspidhfj.json
traj_lieyyspidhfj.md
traj_lieyyspidhfj.trace.json
traj_m7mpv7j8n78h.json
traj_m7mpv7j8n78h.md
traj_m9sgbxehh8my/
summary.md
trajectory.json
traj_mi9eqd4rjfea.json
traj_mi9eqd4rjfea.md
traj_mutgtir4ce8k/
summary.md
trajectory.json
traj_mytnzgfayj3d.json
traj_mytnzgfayj3d.md
traj_mz5m5ysjj31e.json
traj_mz5m5ysjj31e.md
traj_n0qwpjvmdl2s.json
traj_n0qwpjvmdl2s.md
traj_n8duofq5vq1a.json
traj_n8duofq5vq1a.md
traj_new9hq87ca49/
summary.md
trajectory.json
traj_o251whkvy9rl.json
traj_o251whkvy9rl.md
traj_o9cx33xn5u39.json
traj_o9cx33xn5u39.md
traj_ootb5rt3tozd.json
traj_ootb5rt3tozd.md
traj_oyc528j7suvo.json
traj_oyc528j7suvo.md
traj_p2vqjz2scihm/
summary.md
trajectory.json
traj_p561cnoi6gm5/
summary.md
trajectory.json
traj_paoj645u94py/
summary.md
trajectory.json
traj_piik8r6zu3i7.json
traj_piik8r6zu3i7.md
traj_pmrcfj6or3pz.json
traj_pmrcfj6or3pz.md
traj_q2r3c9dmdep7.json
traj_q2r3c9dmdep7.md
traj_qb7dzhsipy3d/
summary.md
trajectory.json
traj_qbq3laxbvhzf.json
traj_qbq3laxbvhzf.md
traj_qi2yqsjp90y3/
summary.md
trajectory.json
traj_qtmid2nzz0kz.json
traj_qtmid2nzz0kz.md
traj_rctcvl6hu15c/
summary.md
trajectory.json
traj_ryf5sstno6p3.json
traj_ryf5sstno6p3.md
traj_rzijuhsjdsae/
summary.md
trajectory.json
traj_s5eyi5jw2q1u/
summary.md
trajectory.json
traj_s5ojo1f4srz4.json
traj_s5ojo1f4srz4.md
traj_s5tuzcraukmz/
summary.md
trajectory.json
traj_sh2ahp9z2xg6.json
traj_sh2ahp9z2xg6.md
traj_sqerp89tc436.json
traj_sqerp89tc436.md
traj_t5uknesn2fcw.json
traj_t5uknesn2fcw.md
traj_t6h534vn0bpg.json
traj_t6h534vn0bpg.md
traj_tavtex0db4b0.json
traj_tavtex0db4b0.md
... 1600 moreShowing a partial view of a very large repo.
FAQ
relay is a Claude Code plugin with 17 hand-picked skills for automation work, indexed on Flowy. Install it with the command on its page. It includes browser-testing-with-screenshots, choosing-swarm-patterns, creating-claude-agents-skill. Its skills do not fire on their own yet. Request auto-invocation to have Flowy route them as you prompt. Free and open source.