Quickstart

Create a workspace, register sessions, send messages, and expose a Zod-backed action.

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 zod

Add harness packages only when Relay needs to create or manage sessions for you.

npm install @agent-relay/runtime @agent-relay/harnesses

Create A Workspace

relay.ts
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.

sessions.ts
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

kickoff.ts
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

listeners.ts
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.

actions.ts
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.

runtime-actions.ts
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