TypeScript SDK

Start sessions, stream records, and invoke agents from TypeScript.

The TypeScript SDK is a client for the /v1 API with session handles, streaming, pagination, and typed errors.

Install

1
npm install @ellipsis-dev/sdk

Start a session

1
import { Ellipsis } from '@ellipsis-dev/sdk';
2
3
const client = new Ellipsis({
4
apiKey: process.env.ELLIPSIS_API_TOKEN!,
5
});
6
const handle = await client.sessions.run({
7
claude_code: {
8
model: 'claude-opus-5-5',
9
prompt: 'Run the tests and report failures.',
10
},
11
environment: 'api-environment',
12
conversation: { interactive: false },
13
budget: { session: 3 },
14
});
15
const turn = await handle.wait({ timeoutMs: 900_000 });
16
console.log(turn?.status, turn?.reason, turn?.detail);

run returns a handle while work continues. wait polls until the turn that answers the prompt reaches a final status (completed, failed, stopped, or cancelled) and returns that turn, or null when the session has no turn to wait on. A timeout throws and leaves the session running.

Use await client.sessions.handle(sessionId) to attach to an existing session.

Continue a conversation

Leave conversation.interactive enabled to accept messages:

1
const conversation = await client.sessions.run({
2
claude_code: {
3
model: 'claude-opus-5-5',
4
prompt: 'Investigate the failing validation test.',
5
},
6
environment: 'api-environment',
7
budget: { session: 5 },
8
});
9
await conversation.wait();
10
const { turn } = await conversation.send('Add a regression test.', {
11
idempotencyKey: 'add-test',
12
});
13
const answered = await conversation.wait();
14
console.log(turn.id, answered?.status);

send returns the message and the turn that answers it, and wait then waits on that turn. The same message key is accepted once per session. A message sent while a turn is running is answered after it. stop() ends the running turn with stopped; it fails with 409 when no turn is pending or running.

Start an agent session

Start an agent session as written:

1
const { session } = await client.agents.start('test-repair');
2
const handle = await client.sessions.handle(session.id);
3
const result = await handle.wait();
4
console.log(result?.status, result?.detail);

Send any field of a session start to replace the agent's value for that session:

1
await client.agents.start('test-repair', { budget: { session: 5 } });

For an agent with an input schema, pass input:

1
await client.agents.start('classify-change', {
2
input: { description: 'Reject expired reset tokens' },
3
});

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:

1
import WebSocket from 'ws';
2
import { streamSession, type OpenSocket } from '@ellipsis-dev/sdk/stream';
3
4
const token = process.env.ELLIPSIS_API_TOKEN!;
5
const openSocket: OpenSocket = ({ sessionId, query }) => {
6
const path = '/v1/sessions/' + encodeURIComponent(sessionId) + '/stream';
7
const ws = new WebSocket('wss://api.ellipsis.dev' + path + '?' + query, {
8
headers: { authorization: 'Bearer ' + token },
9
});
10
return {
11
onOpen: (cb) => ws.on('open', cb),
12
onMessage: (cb) => ws.on('message', (raw) => cb(raw.toString())),
13
onClose: (cb) => ws.on('close', (code) => cb(code)),
14
onError: (cb) => ws.on('error', (error) => cb(error)),
15
close: () => ws.close(),
16
};
17
};
18
19
const outcome = await streamSession({
20
sessionId: handle.id,
21
openSocket,
22
onFrame: (frame) => console.log(frame.type),
23
});
24
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 turns

1
const { turns, messages } = await client.sessions.turns.list(handle.id);
2
for (const turn of turns) {
3
console.log(turn.index, turn.status, turn.reason, turn.cost.total);
4
}
5
for (const message of messages) {
6
console.log(message.turn_id, message.status, message.body);
7
}

turns.list returns every turn and message of a session, oldest first; turns.get(sessionId, turnId) returns one turn.

Read records

1
for await (const record of await client.sessions.records(handle.id)) {
2
if (record.kind === 'platform') {
3
console.log(record.record_type, record.payload);
4
} else if (record.kind === 'claude_code') {
5
console.log(record.payload);
6
} else if (record.kind === 'codex' || record.kind === 'unknown') {
7
console.log(record.record_format, record.payload);
8
}
9
}

Native records preserve their original payloads. Handle unknown variants so new event types do not break your consumer.

Pagination and errors

1
import { APIError } from '@ellipsis-dev/sdk';
2
3
try {
4
for await (const session of await client.sessions.list({ days: 7 })) {
5
console.log(session.id, session.conversation.state);
6
}
7
} catch (error) {
8
if (!(error instanceof APIError)) throw error;
9
console.error(error.status, error.code);
10
}

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 2 retries for transport errors, 429, 502, 503, and 504. Configure timeoutMs and maxRetries when creating it.

On this page

Schedule a demo