@synadia-ai/agents
v0.5.2
Published
TypeScript SDK for the Synadia Agent Protocol for NATS — discover, prompt, and stream from AI agents over NATS.
Readme
@synadia-ai/agents
Caller-side TypeScript SDK for the Synadia Agent Protocol for NATS. Discover, prompt, and stream from AI agents over NATS.
- Catch errors before they hit the wire. Oversized payloads and unsupported attachments are validated locally against the agent's advertised limits — and against the caller's own
nc.info.max_payload, so the smaller of the two binds (a caller behind a smaller-cap broker fails fast instead of waiting forMAX_PAYLOAD_VIOLATION). - Stream responses with
for await. Prompts return typed chunks (response,status,query) you iterate asynchronously. - Runs on Node ≥ 20 and Bun ≥ 1.2.
Hosting an agent? Install the sister package
@synadia-ai/agent-serviceforAgentService,ReferenceAgent, and the host-side wire helpers. The two packages release in lockstep.
Install
bun add @synadia-ai/agents
# or: npm install @synadia-ai/agents
# or: pnpm add @synadia-ai/agents30-second quickstart
You bring a NatsConnection; the SDK uses it. Use @nats-io/transport-node for
TCP (nats://, tls://) or wsconnect from @nats-io/nats-core for WebSocket
(ws://, wss://) — the same connection can then be shared with JetStream, KV,
services, and anything else in the @nats-io/* ecosystem.
import { connect } from "@nats-io/transport-node";
import { Agents } from "@synadia-ai/agents";
const nc = await connect({ servers: "nats://localhost:4222" });
const agents = new Agents({ nc });
const found = await agents.discover(); // stall strategy — returns as soon as replies quiet down
for await (const msg of await found[0]!.prompt("describe this photo", {
attachments: ["./vacation.jpg"],
})) {
if (msg.type === "response") process.stdout.write(msg.text);
}
await agents.close();
await nc.close(); // caller owns the NATS connectionLocal validation in action
If the target agent doesn't accept attachments, or if the envelope exceeds its max_payload, the SDK fails your call before publishing:
import { AttachmentsNotSupportedError, PayloadTooLargeError } from "@synadia-ai/agents";
try {
const stream = await remote.prompt("describe this photo", {
attachments: ["./vacation.jpg"],
});
for await (const msg of stream) {
/* ... */
}
} catch (e) {
if (e instanceof AttachmentsNotSupportedError) {
// agent's attachments_ok === false - no wire traffic.
} else if (e instanceof PayloadTooLargeError) {
console.log(`${e.actual} > ${e.limit} bytes`);
// Again: no wire traffic.
} else throw e;
}Both error types extend ValidationError → NatsAgentError. See Error handling for the full taxonomy.
What's in the box
| API | Purpose |
| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| new Agents({ nc, ... }) | Construct from a caller-owned NatsConnection. |
| agents.discover({filter?, timeoutMs?}) | Return a live Agent[]; auto subscribe-before-ping (§8.5). |
| agent.prompt(text, {attachments, signal, inactivityTimeoutMs}) | Return a PromptStream. |
| agents.liveness(id) / onHeartbeat(id, cb) / ping(id) | Heartbeat tracking and on-demand ping. |
| agents.close() | Tear down SDK state; aborts all in-flight streams. |
| loadContextOptions(name) / parseNatsUrl(url) | Bridge nats CLI context files / URLs into NodeConnectionOptions for connect(). |
| withAgentReconnectDefaults(opts?) | Opt-in resilient reconnect defaults for agent runtimes — see below. Pure transform. |
Resilient reconnect defaults
@nats-io/transport-node gives up after ~10 reconnect attempts (~20 seconds) by default, which is too aggressive for an agent process that's supposed to outlive laptop sleeps and intermittent broker reachability. Wrap your options with withAgentReconnectDefaults to keep retrying indefinitely (and to retry from the very first connect attempt, instead of throwing if the broker is down at startup):
import { connect } from "@nats-io/transport-node";
import { withAgentReconnectDefaults } from "@synadia-ai/agents";
const nc = await connect(withAgentReconnectDefaults({ servers: "nats://localhost:4222" }));The helper merges defaults into caller-provided options, preserving any field the caller explicitly set (including 0 and false). The defaults — maxReconnectAttempts: -1, reconnectTimeWait: 2000, reconnectJitter: 200, waitOnFirstConnect: true — are also exported as AGENT_RECONNECT_DEFAULTS for introspection or selective override.
When you adopt these defaults, also handle the terminal close status event in your for await (const s of nc.status()) loop — it still fires on repeated identical auth errors and on explicit nc.close() / nc.drain(), and a stuck "reconnecting…" UI on a truly dead connection is worse than an honest "disconnected" one.
Subpath exports:
@synadia-ai/agents/errors- the error class hierarchy, for targetedinstanceofbranches.
The host-side ReferenceAgent previously available at @synadia-ai/agents/testing moved to @synadia-ai/agent-service/testing when the SDK split into caller + host packages. Anything you used to import from there is in the new sister package now.
Documentation
- Getting started - end-to-end walkthrough with error handling, cancellation, and liveness.
- Protocol mapping - every SDK call cross-referenced to the spec.
examples/- five runnable scripts (discover, prompt-text, prompt-attachment, query-reply, liveness).
Browser support is planned but not shipped yet - the core validation and parsing layers are already runtime-agnostic.
Contributing
bun install # or: npm install
bun run typecheck
bun run lint
bun run test:unit # no NATS required
bun run test:integration # spawns nats-server - install via brew / apt / https://github.com/nats-io/nats-server/releasesIntegration tests skip cleanly with a friendly message if nats-server isn't on PATH.
License
Apache-2.0
