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.