TypeScript SDK Reference

Complete reference for the @agent-relay/sdk package

npm install @agent-relay/sdk

AgentRelay

The main entry point. Manages the broker lifecycle, spawns agents, and routes messages.

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

const relay = new AgentRelay(options?: AgentRelayOptions);

AgentRelayOptions

PropertyTypeDescriptionDefault
binaryPathstringPath to the broker binaryAuto-resolved
binaryArgsstring[]Extra arguments for the broker processNone
brokerNamestringName for the broker instanceAuto-generated
channelsstring[]Default channels agents are joined to on spawn['general']
cwdstringWorking directory for the broker and spawned agentsprocess.cwd()
envNodeJS.ProcessEnvEnvironment variables passed to the brokerInherited
workspaceNamestringName for the auto-created Relaycast workspaceRandom
relaycastBaseUrlstringBase URL for the Relaycast APIhttps://api.relaycast.dev

Spawning Agents

Shorthand Spawners

// Spawn by CLI type
const agent = await relay.claude.spawn(options?)
const agent = await relay.codex.spawn(options?)
const agent = await relay.gemini.spawn(options?)
const agent = await relay.opencode.spawn(options?)

Spawn options:

PropertyTypeDescription
namestringAgent name (defaults to CLI name)
modelstringModel to use (see Models below)
taskstringInitial task / prompt
channelsstring[]Channels to join
argsstring[]Extra CLI arguments
cwdstringWorking directory override
onStartfunctionSync/async callback before spawn request is sent
onSuccessfunctionSync/async callback after spawn succeeds
onErrorfunctionSync/async callback when spawn fails

relay.spawn(name, cli, task?, options?)

Spawn any CLI by name:

const agent = await relay.spawn('Worker', 'claude', 'Help with refactoring', {
  model: Models.Claude.SONNET,
  channels: ['team'],
});

relay.spawnAndWait(name, cli, task, options?)

Spawn and wait for the agent to be ready before returning:

const agent = await relay.spawnAndWait('Worker', 'claude', 'Analyze the codebase', {
  timeoutMs: 30000,
  waitForMessage: false, // true = wait for first message, false = wait for process ready
});

Agent

All spawn methods return an Agent:

interface Agent {
  readonly name: string;
  readonly runtime: AgentRuntime;
  readonly channels: string[];
  readonly status: AgentStatus; // 'spawning' | 'ready' | 'idle' | 'exited'
  exitCode?: number;
  exitSignal?: string;
  exitReason?: string;

  sendMessage(input: {
    to: string;
    text: string;
    threadId?: string;
    priority?: number;
    data?: Record<string, unknown>;
  }): Promise<Message>;

  release(reasonOrOptions?: string | ReleaseOptions): Promise<void>;
  waitForReady(timeoutMs?: number): Promise<void>;
  waitForExit(timeoutMs?: number): Promise<'exited' | 'timeout' | 'released'>;
  waitForIdle(timeoutMs?: number): Promise<'idle' | 'timeout' | 'exited'>;
  onOutput(callback: (chunk: string) => void): () => void; // returns unsubscribe
}

ReleaseOptions

agent.release(...) accepts either a reason string or a ReleaseOptions object:

PropertyTypeDescription
reasonstringOptional release reason sent to the broker
onStartfunctionSync/async callback before release request is sent
onSuccessfunctionSync/async callback after release succeeds
onErrorfunctionSync/async callback when release fails

Human Handles

Send messages from a named human or system identity (not a spawned CLI agent):

// Named human
const human = relay.human({ name: 'Orchestrator' });
await human.sendMessage({ to: 'Worker', text: 'Start the task' });

// System identity (name: "system")
const sys = relay.system();
await sys.sendMessage({ to: 'Worker', text: 'Stop and report status' });

// Broadcast to all agents
await relay.broadcast('All hands: stand by for new task');

Event Hooks

Assign a function to subscribe, null to unsubscribe:

relay.onMessageReceived = (msg: Message) => { ... }
relay.onMessageSent    = (msg: Message) => { ... }
relay.onAgentSpawned   = (agent: Agent) => { ... }
relay.onAgentReleased  = (agent: Agent) => { ... }
relay.onAgentExited    = (agent: Agent) => { ... }
relay.onAgentReady     = (agent: Agent) => { ... }
relay.onAgentIdle      = ({ name, idleSecs }) => { ... }
relay.onAgentExitRequested = ({ name, reason }) => { ... }
relay.onWorkerOutput   = ({ name, stream, chunk }) => { ... }
relay.onDeliveryUpdate = (event: BrokerEvent) => { ... }

Message type:

interface Message {
  eventId: string;
  from: string;
  to: string;
  text: string;
  threadId?: string;
  data?: Record<string, unknown>;
}

Other Methods

// List all known agents
const agents = await relay.listAgents(): Promise<Agent[]>

// Get broker status
const status = await relay.getStatus(): Promise<BrokerStatus>

// Read last N lines of an agent's log file
const logs = await relay.getLogs('Worker', { lines: 100 })
// logs.found: boolean, logs.content: string

// List agents that have log files
const names = await relay.listLoggedAgents(): Promise<string[]>

// Stream an agent's log file (returns handle with .unsubscribe())
const handle = relay.followLogs('Worker', {
  historyLines: 50,
  onEvent(event) {
    if (event.type === 'log') console.log(event.content);
  },
})
handle.unsubscribe();

// Wait for the first of many agents to exit
const { agent, result } = await AgentRelay.waitForAny([agent1, agent2], 60000)

// Shut down all agents and the broker
await relay.shutdown()

Complete Example

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

const relay = new AgentRelay();

relay.onMessageReceived = (msg) => {
  console.log(`${msg.from} → ${msg.to}: ${msg.text}`);
};

relay.onAgentSpawned = (agent) => {
  console.log(`Spawned: ${agent.name}`);
};

// Spawn agents
const planner = await relay.claude.spawn({
  name: 'Planner',
  model: Models.Claude.OPUS,
  task: 'Plan the feature implementation',
});

const coder = await relay.codex.spawn({
  name: 'Coder',
  model: Models.Codex.GPT_5_3_CODEX,
  task: 'Implement the plan',
});

// Wait for both to be ready
await planner.waitForReady();
await coder.waitForReady();

// Send a message
await planner.sendMessage({ to: 'Coder', text: 'Start implementing the auth module' });

// Wait for coder to finish
await coder.waitForExit(300_000);

await relay.shutdown();

Models

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

// Claude
Models.Claude.OPUS; // 'opus'
Models.Claude.SONNET; // 'sonnet'
Models.Claude.HAIKU; // 'haiku'

// Codex
Models.Codex.GPT_5_4; // 'gpt-5.4' (default)
Models.Codex.GPT_5_3_CODEX; // 'gpt-5.3-codex'

// Gemini
Models.Gemini.GEMINI_3_1_PRO_PREVIEW; // 'gemini-3.1-pro-preview' (default)
Models.Gemini.GEMINI_2_5_PRO; // 'gemini-2.5-pro'

// OpenCode
Models.Opencode.OPENAI_GPT_5_2; // 'openai/gpt-5.2' (default)
Models.Opencode.OPENCODE_GPT_5_NANO; // 'opencode/gpt-5-nano'

Error Types

import { AgentRelayProtocolError, AgentRelayProcessError } from '@agent-relay/sdk';

try {
  await relay.claude.spawn({ name: 'Worker' });
} catch (err) {
  if (err instanceof AgentRelayProtocolError) {
    // Broker returned an error response (err.code available)
  }
  if (err instanceof AgentRelayProcessError) {
    // Broker process failed to start or crashed
  }
}

See Also