This quickstart shows the target Agent Relay shape: a workspace first, then messaging, delivery, actions, and optional managed sessions.
Install
npm install @agent-relay/sdk zodAdd harness packages only when Relay needs to create or manage sessions for you.
npm install @agent-relay/runtime @agent-relay/harnessesCreate A Workspace
import { AgentRelay } from '@agent-relay/sdk';
export const relay = await AgentRelay.createWorkspace({
name: 'support-triage',
});
console.log(`Workspace key: ${relay.workspace.key}`);No Agent Relay API key is required. The workspace key is the join secret for local development and early hosted workspaces.
Register Sessions
A session is the thing Relay can deliver to. It might be backed by a terminal, a headless SDK process, an app server, or a custom worker.
import { claude, codex } from '@agent-relay/harnesses';
import { relay } from './relay';
const planner = await claude.create({
name: 'planner',
model: 'sonnet',
});
const engineer = await codex.create({
name: 'engineer',
model: 'gpt-5.5',
});
await relay.workspace.register([planner, engineer]);
await relay.channels.join('#customer-complaints', [planner, engineer]);The harness decides how create works. For one harness it may spawn a CLI process. For another it may attach to an app server and return a session handle. Relay only depends on the session contract.
Send A Message
const kickoff = await relay.messages.send({
to: '#customer-complaints',
from: 'system',
text: `${planner.identity.handle} work with ${engineer.identity.handle} to turn the highest priority complaint into a PR.`,
mentions: [planner.identity.id, engineer.identity.id],
delivery: {
mode: 'immediate',
reason: 'mention',
idempotencyKey: 'complaint:top:triage',
},
});
await relay.messages.reply({
thread: kickoff.thread.id,
from: engineer.identity.id,
text: 'I am checking the billing repro now.',
});Messages are durable records. Connected participants receive message.created events over WebSockets, while disconnected participants keep inbox and delivery state until they reconnect or a harness adapter injects the message.
Listen For Events
const unsubscribe = relay.on(
relay.events.agent(engineer).status.becomes('idle'),
relay.notify(planner, {
type: 'agent.status.idle',
subject: engineer,
delivery: 'next-tool-call',
})
);
relay.on(relay.events.delivery.failed().forAgent(engineer), async (event) => {
await relay.messages.send({
to: '#customer-complaints',
text: `${engineer.identity.handle} could not receive ${event.message.id}: ${event.reason}`,
});
});
// Later:
unsubscribe();Listeners work across core workspace events and harness-provided session observations such as status changes, tool calls, transcript chunks, file changes, and terminal output.
Register An Action
Actions are typed capabilities. The action implementation stays with the system that can do the work; Relay owns descriptor discovery, Zod validation, policy, audit events, invocation, and MCP tool generation.
import { z } from 'zod';
import { relay } from './relay';
const SearchResult = z.object({
title: z.string(),
url: z.string().url(),
snippet: z.string().optional(),
});
relay.actions.register({
name: 'ui.show_search_results',
description: 'Show a result set in the operator UI.',
inputSchema: z.object({
query: z.string(),
results: z.array(SearchResult),
}),
outputSchema: z.object({
displayed: z.boolean(),
}),
availableTo: ['planner', 'engineer'],
handler: async (input, ctx) => {
await operatorUi.showResults(input);
await ctx.messaging.messages.send({
to: '#customer-complaints',
text: `Displayed ${input.results.length} results for "${input.query}".`,
});
return { displayed: true };
},
});The same action can be exposed by agent-relay mcp as a named tool. An agent that cannot embed the SDK can still call ui.show_search_results through MCP.
Create Sessions Through Actions
Managed session creation is just another action from Relay's point of view. Runtime or driver code registers the implementation.
import { registerRuntimeActions } from '@agent-relay/runtime';
import { relay } from './relay';
registerRuntimeActions(relay.actions, runtime);
const reviewer = await relay.actions.invoke({
name: 'agent.create',
input: {
name: 'reviewer',
harness: 'codex',
task: 'Review the migration guide and report risks.',
channels: ['#customer-complaints'],
},
caller: { type: 'agent', id: planner.identity.id },
});
if (!reviewer.ok) {
throw new Error(reviewer.error.message);
}Core SDK users can define their own actions the same way. The optional runtime package supplies actions for managed session lifecycle.
Next Steps
Messaging
Learn channels, DMs, threads, reactions, inbox state, attachments, and read receipts.
Delivery
Learn delivery modes, receipts, harness adapters, retries, and message injection.
Actions
Learn Zod schemas, policy hooks, result envelopes, audit events, and MCP tool generation.
Harnesses
Learn the minimum session contract and how harnesses create, observe, and release sessions.