@aramb-ai/sdk
v0.0.10
Published
Typed Node/TypeScript SDK for the aramb agent gateway — create agents, stream replies, read usage.
Readme
@aramb-ai/sdk
The Node/TypeScript SDK for the aramb agent gateway. Three primitives — create an agent, talk to it (streaming or awaited), read usage — over a small, fully-typed surface.
npm install @aramb-ai/sdkRequires Node 18+ (uses the built-in fetch). Server-side only: your apiKey is sent on
every request and must never ship to a browser.
The three primitives
import { Aramb } from '@aramb-ai/sdk';
const aramb = new Aramb({ apiKey: process.env.ARAMB_KEY! });new Aramb({ apiKey, baseUrl? }) — baseUrl defaults to the hosted gateway
(https://gateway.aramb.dev); pass it only to target another environment.
1. Create an agent — createAgent
Define an agent once (e.g. at deploy time) from its identity. The systemPrompt is the
agent — the persona it embodies, not instructions layered onto a generic assistant — so write
it as who the agent is. Returns an agentId you reuse for every conversation.
const { agentId } = await aramb.createAgent({
systemPrompt: 'You are a supportive running coach. Keep replies under 4 sentences.',
name: 'coach', // optional
isolationMode: 'per_user', // optional; server default
});isolationMode controls how your end-users are separated under one agent:
per_user(default) — each end-user (subTenant) gets their own project + VM + isolated transcript.shared— all end-users share one project (and one VM).
Omit it and the server applies per_user.
2. Talk to it — session(...)
A session is one conversation, scoped to a single end-user via subTenant (your own
stable per-user id — used for isolation and usage accounting). It owns a WebSocket to the
gateway, dialed lazily on the first call. Optionally pass subTenantDisplay — a
human-readable label (email/username) shown next to the opaque subTenant in the console;
it never becomes the isolation or meter key.
// subTenant = your stable per-user id; subTenantDisplay = a readable label for the console.
const session = aramb.session({ agentId, subTenant: user.id, subTenantDisplay: user.email });
// Awaited: get the whole reply at once.
const { reply, usage } = await session.run('I ran 5k today, how do I recover?');
console.log(reply); // full text
console.log(usage?.tokens); // token count, when the gateway reports it
// Streaming: render tokens as they arrive.
for await (const chunk of session.stream('Plan my week.')) {
process.stdout.write(chunk.delta);
}
await session.close(); // release the socket when the conversation is doneRuns on one session are serialized: calling run/stream again before the previous
finishes queues it, so deltas never interleave. Use one session per concurrent user.
3. Read usage — getUsage
const all = await aramb.getUsage(); // every product + sub-tenant
const one = await aramb.getUsage({ subTenant: 'user-42' }); // scoped to one user
// { products: [{ product, subTenants: [{ subTenant, tokens }] }] }Frame & event model
Under the hood the gateway streams typed frames. run/stream consume the reply
frames for you; lifecycle frames are surfaced as events on the session:
session.on('status', (state) => {}); // 'provisioning' | 'ready' | 'reconnecting' | 'failed'
session.on('turn', (phase) => {}); // 'start' | 'end'
session.on('error', (err) => {}); // ArambError — also rejects the in-flight run/stream| Frame | Surfaced as |
|-------|-------------|
| status | status event |
| turn | turn event |
| chunk | joined into run()'s reply / yielded by stream() as .delta |
| done | resolves run() / ends stream() |
| error | error event and rejects the in-flight call |
The session reconnects automatically on a transient socket drop mid-run (emitting
status: 'reconnecting' and resending the input); after the reconnect budget is exhausted
it rejects with code: 'connection_failed'.
Error handling
Every failure — HTTP, socket, or an error frame — throws an ArambError with a stable,
machine-readable code (e.g. unauthorized, over_quota, connection_failed). Transport
internals are never leaked.
import { ArambError, isArambError } from '@aramb-ai/sdk';
try {
await session.run('hi');
} catch (err) {
if (isArambError(err) && err.code === 'unauthorized') {
// handle a bad/expired key
}
}Quickstart — a coaching chat backend
A complete, runnable Express server: one shared agent, one session per user, streaming and awaited endpoints, plus a usage route. Copy it as-is.
import express from 'express';
import { Aramb, type Session } from '@aramb-ai/sdk';
const aramb = new Aramb({ apiKey: process.env.ARAMB_KEY! });
// Create the agent once at startup; reuse its id for everyone. The system
// prompt is the agent's identity — the persona it is, not a decoration block.
const { agentId } = await aramb.createAgent({
systemPrompt: 'You are a supportive running coach. Keep replies under 4 sentences.',
});
// One session per end-user, keyed by your app's user id (the subTenant).
const sessions = new Map<string, Session>();
function sessionFor(userId: string): Session {
let s = sessions.get(userId);
if (!s) {
s = aramb.session({ agentId, subTenant: userId });
s.on('error', (err) => console.error(`session ${userId}:`, err.code, err.message));
sessions.set(userId, s);
}
return s;
}
const app = express();
app.use(express.json());
// Awaited reply.
app.post('/chat/:userId', async (req, res) => {
try {
const { reply } = await sessionFor(req.params.userId).run(req.body.message);
res.json({ reply });
} catch (err: any) {
res.status(502).json({ error: err.code ?? 'unknown', message: err.message });
}
});
// Streamed reply (Server-Sent Events).
app.post('/chat/:userId/stream', async (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
try {
for await (const chunk of sessionFor(req.params.userId).stream(req.body.message)) {
res.write(`data: ${JSON.stringify(chunk.delta)}\n\n`);
}
} catch (err: any) {
res.write(`event: error\ndata: ${JSON.stringify(err.code ?? 'unknown')}\n\n`);
}
res.end();
});
// Per-user token usage.
app.get('/usage/:userId', async (req, res) => {
res.json(await aramb.getUsage({ subTenant: req.params.userId }));
});
app.listen(3000, () => console.log('coach backend on :3000'));Full method reference: docs/api.md.
