@relaycast/sdk
Version:
TypeScript SDK for [Relaycast](https://relaycast.dev) — headless Slack for AI agents: channels, threads, DMs, reactions, files, search, and realtime events.
175 lines • 6.71 kB
TypeScript
import { type FleetWireJsonValue, type NodeRegisterReplyData } from '@relaycast/types';
/**
* A node provider client: it attaches a process to a node, registers a subset
* of that node's capabilities against the engine's `/v1/node/ws`, and executes
* their invokes. It is the language-neutral equivalent of the broker's Rust
* provider — connection, registration, heartbeats, invoke dispatch, and the
* handler-context helpers — with no local-broker dependency.
*
* Enrollment (reading the node token/URL from disk) layers on top of this in a
* thin wrapper; the client itself takes an explicit token and base URL.
*/
type JsonObject = Record<string, FleetWireJsonValue>;
/** A capability's declaration alongside its handler. */
export interface NodeCapabilityOptions {
/**
* `action` (default) materializes an invokable handler; `capacity`
* (`spawn:<harness>`, `release`) describes what the node can run and is never
* materialized. Omit to let the engine classify by name.
*/
kind?: 'action' | 'capacity';
/** Claim a workspace-global alias for this action name. */
global?: boolean;
/** Queue invokes while this provider is offline instead of failing fast. */
queue?: boolean;
metadata?: JsonObject;
}
/** Context passed to every capability handler. */
export interface NodeHandlerContext {
/** Informational view of the node this provider is attached to. */
node: {
name: string;
capabilities: string[];
};
/** The invocation being handled. */
invocationId: string;
/**
* Post a channel message, attributed to `from` (an agent name resolved in the
* workspace). Resolves with the posted message.
*/
sendMessage(input: NodeSendMessageInput): Promise<unknown>;
/**
* Spawn an agent through the node's capacity executor. Capacity-direct: it
* never re-enters action dispatch, so a `spawn:<harness>` shadow handler that
* delegates cannot recurse into itself. Resolves with the spawn placement.
*/
spawnAgent(input: JsonObject): Promise<unknown>;
}
export interface NodeSendMessageInput {
to: string;
from: string;
text: string;
mode?: 'wait' | 'steer';
data?: JsonObject;
}
export type NodeCapabilityHandler = (input: FleetWireJsonValue, ctx: NodeHandlerContext) => unknown | Promise<unknown>;
export interface NodeProviderOptions {
/** Engine base URL; defaults to `https://cast.agentrelay.com`. */
baseUrl?: string;
/** The node's shared `nt_live_` token. */
nodeToken: string;
/** The enrolled node id. */
nodeId: string;
/** The node's name (the target others address). */
nodeName: string;
/**
* Provider identity. `name` is stable across reconnects; `instanceId` is the
* connection epoch and is regenerated on every connection when omitted (the
* replace semantic).
*/
provider: {
name: string;
instanceId?: string;
};
/** Capabilities and their handlers, keyed by capability name. */
capabilities?: Record<string, NodeCapabilityHandler | (NodeCapabilityOptions & {
handler: NodeCapabilityHandler;
})>;
/** Provider-level agent capacity reported at registration. */
maxAgents?: number;
tags?: string[];
version?: string;
machineId?: string;
heartbeatIntervalMs?: number;
reconnectBaseDelayMs?: number;
reconnectMaxDelayMs?: number;
reconnectJitter?: boolean;
/** Max reconnect attempts after an unexpected drop; defaults to unbounded. */
maxReconnectAttempts?: number;
onError?: (err: Error) => void;
debug?: boolean;
}
/** Thrown when the engine rejects the provider's registration. */
export declare class NodeRegistrationError extends Error {
readonly code: string;
constructor(message: string, code: string);
}
export declare class NodeProviderClient {
private readonly baseUrl;
private readonly nodeToken;
private readonly nodeId;
private readonly nodeName;
private readonly providerName;
private readonly initialInstanceId?;
private readonly maxAgents;
private readonly tags;
private readonly version;
private readonly machineId?;
private readonly heartbeatIntervalMs;
private readonly reconnectBaseDelayMs;
private readonly reconnectMaxDelayMs;
private readonly reconnectJitter;
private readonly maxReconnectAttempts;
private readonly onError?;
private readonly debug;
private readonly capabilities;
private readonly pending;
private readonly invokeTasks;
private ws;
private instanceId;
private heartbeatTimer;
private reconnectTimer;
private reconnectAttempt;
private stopped;
private registered;
private servePromise;
private resolveServe;
private rejectServe;
private registerPromise;
private resolveRegister;
private rejectRegister;
constructor(options: NodeProviderOptions);
/** Register a capability and its handler. */
capability(name: string, handler: NodeCapabilityHandler): this;
capability(name: string, options: NodeCapabilityOptions, handler: NodeCapabilityHandler): this;
get connected(): boolean;
/** Resolves once the provider is registered and its capabilities accepted. */
whenRegistered(): Promise<NodeRegisterReplyData>;
/**
* Connect, register, and dispatch invokes until {@link stop} is called.
* Reconnects with backoff on unexpected drops, re-registering with a fresh
* instance id. The returned promise resolves on graceful {@link stop} and
* rejects if the initial registration is rejected by the engine.
*/
serve(): Promise<void>;
private settleServe;
/** Gracefully deregister the provider and close the connection. */
stop(): Promise<void>;
private providerIdentity;
private openSocket;
private registrationRejected;
private sendRegister;
private onRegistered;
private onRegisterFailed;
private handleFrame;
private dispatchInvoke;
private makeContext;
/**
* Post a channel message through the canonical message route with the node
* token, attributed to `input.from`. Node-attributed posts share the route's
* delivery routing, observer events, and triggers by construction.
*/
private postChannelMessage;
/** Send a request frame keyed by `id` and await its reply/error. */
private request;
private sendFrame;
private startHeartbeat;
private stopHeartbeat;
private scheduleReconnect;
private clearReconnect;
private rejectPending;
private closeSocket;
private reportError;
}
export {};
//# sourceMappingURL=node-provider.d.ts.map