TypeScript SDK
Start sessions, stream records, and invoke automations from TypeScript.
Install
npm install @ellipsis-dev/sdkStart a session
import { Ellipsis } from '@ellipsis-dev/sdk';
const client = new Ellipsis({
apiKey: process.env.ELLIPSIS_API_TOKEN!,
});
const handle = await client.sessions.run({
harness: { type: 'claude_code' },
environment: 'api-environment',
prompt: 'Run the tests and report failures.',
interactive: false,
budget: 3,
});
const session = await handle.wait({ timeoutMs: 900_000 });
console.log(session.status, session.exit_status);run returns a handle while work continues. wait polls until the session settles. A timeout leaves the session running.
Use await client.sessions.handle(sessionId) to attach to an existing session.
Continue a conversation
Leave interactive enabled to accept messages:
const conversation = await client.sessions.run({
harness: { type: 'claude_code' },
environment: 'api-environment',
prompt: 'Investigate the failing validation test.',
budget: 5,
});
await conversation.wait();
await conversation.send('Add a regression test.', {
idempotencyKey: 'add-test',
});
await conversation.wait();
await conversation.stop();The same message key is accepted once per session. A message during an active turn waits for the next turn.
Invoke an automation
const { session } = await client.automations.run('test-repair', {
prompt: 'Investigate the request validation tests.',
});
const run = await client.sessions.handle(session.id);
await run.wait();For an automation with an input schema, pass input instead:
await client.automations.run('classify-change', {
input: { description: 'Reject expired reset tokens' },
});Stream a session
See Session events for every modeled event and complete JSON examples.
In Node.js, install ws and connect it to the stream adapter:
import WebSocket from 'ws';
import { streamSession, type OpenSocket } from '@ellipsis-dev/sdk/stream';
const token = process.env.ELLIPSIS_API_TOKEN!;
const openSocket: OpenSocket = ({ sessionId, query }) => {
const path = '/v1/sessions/' + encodeURIComponent(sessionId) + '/stream';
const ws = new WebSocket('wss://api.ellipsis.dev' + path + '?' + query, {
headers: { authorization: 'Bearer ' + token },
});
return {
onOpen: (cb) => ws.on('open', cb),
onMessage: (cb) => ws.on('message', (raw) => cb(raw.toString())),
onClose: (cb) => ws.on('close', (code) => cb(code)),
onError: (cb) => ws.on('error', (error) => cb(error)),
close: () => ws.close(),
};
};
const outcome = await streamSession({
sessionId: handle.id,
openSocket,
onFrame: (frame) => console.log(frame.type),
});
console.log(outcome.type);Pass the adapter's query unchanged. The SDK handles reconnects and resume cursors. Complete records persist; partial text deltas are live-only.
Read records
for await (const record of await client.sessions.records(handle.id)) {
if (record.kind === 'platform') {
console.log(record.record_type, record.payload);
} else if (record.kind === 'claude_code') {
console.log(record.payload);
} else if (record.kind === 'codex' || record.kind === 'unknown') {
console.log(record.record_format, record.payload);
}
}Native records preserve their original payloads. Handle unknown variants so new event types do not break your consumer.
Pagination and errors
import { APIError } from '@ellipsis-dev/sdk';
try {
for await (const session of await client.sessions.list({ days: 7 })) {
console.log(session.id, session.exit_status);
}
} catch (error) {
if (!(error instanceof APIError)) throw error;
console.error(error.status, error.code);
}Manual pagination exposes items, hasMore, and nextCursor. API fields retain their wire names; SDK options use camelCase.
The client defaults to a 60-second request timeout and two retries for transport errors, 429, 502, 503, and 504. Configure timeoutMs and maxRetries when creating it.