TypeScript SDK

Reference shape for the workspace-first @agent-relay/sdk API: messaging, delivery contracts, actions, events, and session registration.

Install the core SDK:

npm install @agent-relay/sdk zod

The SDK should stay focused on the Agent Relay core protocol. It should not require managed spawning, browser automation, cloud setup, workflow engines, or proactive runtime dependencies.

Entry Point

import { AgentRelay } from '@agent-relay/sdk';

const relay = await AgentRelay.createWorkspace({
  name: 'release-review',
});

Reconnect to an existing workspace with the persisted key:

const relay = new AgentRelay({
  workspaceKey: process.env.RELAY_WORKSPACE_KEY,
});

Workspace API

relay.workspaceKey;
const info = await relay.workspace.info();

// register() returns a LIVE agent client (single in -> single out, array in -> array out)
const planner = await relay.workspace.register({ name: 'planner', type: 'agent' });
const [reviewer, engineer] = await relay.workspace.register([
  { name: 'reviewer', type: 'agent' },
  { name: 'engineer', type: 'agent' },
]);

// rehydrate a client in a fresh process from a persisted token
const planner2 = await relay.workspace.reconnect({ apiToken: planner.token });

Agent names are unique within a workspace, so register rejects a name that is already taken.

Messaging API

Messages are sent from a registered participant — there is no top-level relay.sendMessage or workspace-level relay.messages.send. The live client carries sendMessage, reply, and react.

await planner.channels.create({ name: 'reviews', topic: 'Release review' });
await reviewer.channels.join('reviews');

// to is '#channel', '@handle' (DM), or ['@a','@b'] (group DM)
const { messageId } = await planner.sendMessage({
  to: '#reviews',
  text: `${reviewer.handle} please review the delivery adapter.`,
});

await reviewer.reply({ messageId, text: 'Reviewing now.' });
await planner.react({ messageId, emoji: ':eyes:' });

See Messaging for target, attachment, thread, reaction, and inbox shapes.

Delivery Contracts

type DeliveryMode = 'immediate' | 'next-message' | 'next-tool-call' | 'on-idle' | 'manual';

type MessageContext = {
  id: string;
  mode: DeliveryMode;
  reason: 'message' | 'mention' | 'dm' | 'thread-reply' | 'action-result' | 'notification';
  priority?: 'normal' | 'urgent';
  deadline?: Date | string;
  idempotencyKey?: string;
  metadata?: Record<string, unknown>;
};

type MessageReceipt =
  | { status: 'accepted'; deliveryId: string; retryable?: boolean; metadata?: Record<string, unknown> }
  | { status: 'delivered'; deliveryId: string; metadata?: Record<string, unknown> }
  | { status: 'deferred'; deliveryId?: string; availableAt: Date | string; reason?: string; metadata?: Record<string, unknown> }
  | { status: 'failed'; deliveryId?: string; reason: string; retryable?: boolean; metadata?: Record<string, unknown> };

The SDK can expose DeliveryRunner and AgentDeliveryAdapter interfaces even while durable backend ack, fail, and defer operations are still being implemented. Unsupported operations should fail explicitly.

Harnesses

Spawn real agents with prebuilt harnesses. create({ relay }) spawns and self-registers the agent, returning the live client — no separate register call.

import { claude, codex } from '@agent-relay/harnesses';

const planner = await claude.create({ relay, model: 'sonnet' });
const engineer = await codex.create({ relay, model: 'gpt-5.5' });

See Harnesses for defineHarness, capabilities, and createHuman.

Actions API

Actions are fire-and-forget: invoking returns an acknowledgement immediately, the handler runs in the registering process, and the relay emits action.completed to listeners.

import { z } from 'zod';

relay.registerAction({
  name: 'review.submit_vote',
  description: 'Submit a review vote for the current proposal.',
  input: z.object({
    proposalId: z.string(),
    vote: z.enum(['approve', 'request_changes', 'abstain']),
  }),
  availableTo: [{ name: 'reviewer' }], // omit to allow everyone
  handler: async ({ input, agent }) => {
    await reviewStore.recordVote(agent.name, input);
    return { recorded: true }; // becomes the action.completed payload
  },
});

relay.addListener(relay.action('review.submit_vote').completed(), (event) => {
  console.log(event.output);
});

Action schemas should be Zod schemas. The SDK infers TypeScript types and generates JSON Schema for MCP tools from the same source. See Actions.

Events API

relay.addListener(selector, handler) is the single listener entry point. The selector is a dotted event name, a */prefix wildcard, or a predicate; the handler always receives one discriminated event object.

const unsubscribe = relay.addListener('message.created', async ({ message, envelope }) => {
  if (envelope.channel?.name === 'reviews') {
    await planner.sendMessage({
      to: `@${envelope.from.handle}`,
      text: `Saw your message ${message.messageId}.`,
    });
  }
});

unsubscribe();

The listener system covers message, delivery, action, and normalized session events. See Event handlers and Events.

MCP Integration

The SDK action registry and messaging primitives are what power agent-relay mcp. The agent-relay MCP exposes each registered action as a typed tool and gives agents send_message, reply, join_channel, and friends. For many agents, MCP is the preferred integration path because it gives the agent tools without embedding the SDK into the agent process.

Spawning agents as an action

Agent creation is just another fire-and-forget action. Register one and have the handler spawn through a harness, then message the caller with who showed up.

import { claude } from '@agent-relay/harnesses';
import { z } from 'zod';

relay.registerAction({
  name: 'agent.create',
  description: 'Spawn a managed agent session.',
  input: z.object({ model: z.enum(['opus', 'sonnet']) }),
  handler: async ({ agent: caller, input }) => {
    const agent = await claude.create({ relay, model: input.model });
    await planner.sendMessage({ to: `@${caller.handle}`, text: `Spawned ${agent.handle}` });
    return { agentId: agent.id, handle: agent.handle };
  },
});

This keeps agent creation as a capability in the action protocol instead of a core SDK method.

Compatibility Notes

Older SDKs exposed a "system" relay (relay.sendMessage, relay.system()), token-handoff registration (relay.as(token)), relay.on(...), and a relay.actions namespace. Version 8 replaces those with register-returns-client, agent-scoped sends, relay.addListener(...), and relay.registerAction(...).

Use the migration guide for replacements.