Examples
Complete scripts for the common jobs: run an agent in CI, fan out across repositories, watch a session live, and audit last week's work.
Each script below runs as written, given ELLIPSIS_API_TOKEN in the environment.
Run an agent in CI and fail the job if it fails
import { Ellipsis } from '@ellipsis-dev/sdk';
const client = new Ellipsis({
apiKey: process.env.ELLIPSIS_API_TOKEN!,
});
const handle = await client.sessions.run({
prompt: 'Update the OpenAPI docs to match the routes on this branch',
repository: process.env.GITHUB_REPOSITORY!,
});
console.log(`session ${handle.id}`);
const session = await handle.wait({ timeoutMs: 1_800_000 });
console.log(`finished ${session.status}`);
if (session.status !== 'completed') process.exit(1);wait() rejects after 30 minutes here, which leaves the session running. Exit non-zero on any status other than completed so a failed agent fails the build.
Fan out across repositories, concurrently
import { Ellipsis } from '@ellipsis-dev/sdk';
const client = new Ellipsis({
apiKey: process.env.ELLIPSIS_API_TOKEN!,
});
const repositories = ['acme/api', 'acme/web', 'acme/jobs'];
const prompt = 'Upgrade the pinned Node version to 24 and fix what breaks';
const results = await Promise.all(
repositories.map(async (repository) => {
const handle = await client.sessions.run({ prompt, repository });
const session = await handle.wait();
return { repository, status: session.status };
})
);
for (const { repository, status } of results) {
console.log(`${repository.padEnd(12)} ${status}`);
}Three sessions run in parallel in three sandboxes. Each has its own budget.
Watch a session live
import WebSocket from 'ws';
import { Ellipsis } from '@ellipsis-dev/sdk';
import {
StreamUnavailableError,
streamSession,
type OpenSocket,
type StreamSocket,
} from '@ellipsis-dev/sdk/stream';
const token = process.env.ELLIPSIS_API_TOKEN!;
const client = new Ellipsis({ apiKey: token });
const openSocket: OpenSocket = ({ sessionId, query }): StreamSocket => {
const url = `wss://api.ellipsis.dev/sessions/${encodeURIComponent(sessionId)}/stream?${query}`;
const ws = new WebSocket(url, {
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: number) => cb(code)),
onError: (cb) =>
ws.on('error', (err: unknown) =>
cb(err instanceof Error ? err : new Error(String(err)))
),
close: () => ws.close(),
};
};
const handle = await client.sessions.run({
prompt: 'Fix the flaky test in ci/',
});
try {
const outcome = await streamSession({
sessionId: handle.id,
openSocket,
onFrame: (frame) => {
if (frame.type === 'delta') process.stdout.write(frame.text ?? '');
else if (frame.type === 'session') console.log(`\n[${frame.session.status}]`);
},
});
console.log(`\n${outcome.type}`);
} catch (error) {
if (error instanceof StreamUnavailableError) {
const session = await handle.wait();
console.log(`\npolled to ${session.status}`);
} else {
throw error;
}
}Deltas print as the model produces them; the poll fallback covers an environment where the WebSocket cannot be opened.
Audit last week's sessions
import { Ellipsis } from '@ellipsis-dev/sdk';
const client = new Ellipsis({
apiKey: process.env.ELLIPSIS_API_TOKEN!,
});
const byStatus = new Map<string, number>();
let total = 0;
for await (const session of await client.sessions.list()) {
byStatus.set(session.status, (byStatus.get(session.status) ?? 0) + 1);
total += 1;
}
for (const [status, count] of [...byStatus].sort((a, b) => b[1] - a[1])) {
console.log(`${status.padEnd(12)} ${count}`);
}
console.log(`${total} sessions`);Iterating walks every page, so this reads the full history rather than the first page. Add limit to bound the page size, not the total.
Send a follow-up to a session already running
import { APIError, Ellipsis } from '@ellipsis-dev/sdk';
const client = new Ellipsis({
apiKey: process.env.ELLIPSIS_API_TOKEN!,
});
const handle = await client.sessions.handle(process.env.SESSION_ID!);
try {
await handle.send('Also add a regression test', {
idempotencyKey: 'regression-ask',
});
} catch (error) {
if (error instanceof APIError && error.code === 'session_finished') {
console.log('session already done');
} else {
throw error;
}
}The idempotency key makes a retry of this script safe: the same key delivers the message once.