@ellipsis-dev/sdk 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,97 +1,1227 @@
1
- // src/client.ts
2
- var EllipsisClient = class {
3
- constructor(fetchJson) {
4
- this.fetchJson = fetchJson;
5
- }
6
- fetchJson;
7
- // The enriched public session (§4.1) — the same lean wire shape the
8
- // stream's session frames carry.
9
- async getSession(sessionId) {
10
- return await this.fetchJson(
11
- `/sessions/${encodeURIComponent(sessionId)}`
12
- );
1
+ // src/core/pagination.ts
2
+ var Page = class {
3
+ constructor(response, itemsAttr, fetchNext) {
4
+ this.response = response;
5
+ this.itemsAttr = itemsAttr;
6
+ this.fetchNext = fetchNext;
13
7
  }
14
- // The record log + inbox messages (§4.3). Bare call = the full transcript;
15
- // pass afterSeq/limit to page (limit max 1000, `has_more` signals more).
16
- async getSessionRecords(sessionId, options = {}) {
17
- const params = new URLSearchParams();
18
- if (options.afterSeq != null)
19
- params.set("after_seq", String(options.afterSeq));
20
- if (options.limit != null) params.set("limit", String(options.limit));
21
- const query = params.toString();
22
- return await this.fetchJson(
23
- `/sessions/${encodeURIComponent(sessionId)}/records` + (query ? `?${query}` : "")
24
- );
8
+ response;
9
+ itemsAttr;
10
+ fetchNext;
11
+ get items() {
12
+ return this.response[this.itemsAttr];
25
13
  }
26
- // The session's turns — the structural conversation history (message ->
27
- // response exchanges with status/cost).
28
- async getSessionTurns(sessionId) {
29
- return await this.fetchJson(
30
- `/sessions/${encodeURIComponent(sessionId)}/turns`
31
- );
14
+ get hasMore() {
15
+ return this.response.has_more;
32
16
  }
33
- // The session's executions (initial start, cold wakes, infra retries) with
34
- // their launch context: `system_prompt_append` is the text Ellipsis appends
35
- // to Claude Code's default system prompt, alongside the initial query and
36
- // model. Per execution because each cold wake persists its own launch
37
- // config.
38
- async getSessionExecutions(sessionId) {
39
- return await this.fetchJson(
40
- `/sessions/${encodeURIComponent(sessionId)}/executions`
41
- );
17
+ get nextCursor() {
18
+ return this.response.next_cursor ?? null;
19
+ }
20
+ async *[Symbol.asyncIterator]() {
21
+ let page = this;
22
+ for (; ; ) {
23
+ yield* page.items;
24
+ const cursor = page.nextCursor;
25
+ if (!page.hasMore || cursor == null) return;
26
+ page = await this.fetchNext(cursor);
27
+ }
28
+ }
29
+ };
30
+
31
+ // src/core/sessionHandle.ts
32
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
33
+ "completed",
34
+ "error",
35
+ "cancelled",
36
+ "stopped"
37
+ ]);
38
+ var DEFAULT_POLL_INTERVAL_MS = 3e3;
39
+ function isSettled(session) {
40
+ if (TERMINAL_STATUSES.has(session.status)) return true;
41
+ return session.session_state === "idle" || session.session_state === "closed";
42
+ }
43
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
44
+ var SessionHandle = class {
45
+ constructor(sessions, session) {
46
+ this.sessions = sessions;
47
+ this.id = session.id;
48
+ this.session = session;
49
+ }
50
+ sessions;
51
+ id;
52
+ // The latest session snapshot this handle saw.
53
+ session;
54
+ async refresh() {
55
+ this.session = (await this.sessions.get(this.id)).session;
56
+ return this.session;
57
+ }
58
+ // Poll until the session settles (terminal status, or a parked
59
+ // conversation: state idle/closed).
60
+ async wait(options = {}) {
61
+ const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
62
+ const deadline = options.timeoutMs == null ? null : Date.now() + options.timeoutMs;
63
+ for (; ; ) {
64
+ const session = await this.refresh();
65
+ if (isSettled(session)) return session;
66
+ if (deadline != null && Date.now() >= deadline) {
67
+ throw new Error(
68
+ `session ${this.id} not terminal after ${options.timeoutMs}ms`
69
+ );
70
+ }
71
+ await sleep(pollIntervalMs);
72
+ }
73
+ }
74
+ // Post into the session's inbox (delivered at the next turn boundary;
75
+ // wakes a parked session).
76
+ async send(message, options = {}) {
77
+ const response = await this.sessions.sendMessage(this.id, {
78
+ message,
79
+ idempotency_key: options.idempotencyKey
80
+ });
81
+ return response.message;
82
+ }
83
+ async stop() {
84
+ this.session = (await this.sessions.stop(this.id)).session;
85
+ return this.session;
86
+ }
87
+ };
88
+
89
+ // src/core/errors.ts
90
+ var EllipsisError = class extends Error {
91
+ };
92
+ var TransportError = class extends EllipsisError {
93
+ };
94
+ var APIError = class extends EllipsisError {
95
+ status;
96
+ code;
97
+ requestId;
98
+ body;
99
+ constructor(args) {
100
+ super(`${args.status} ${args.code ?? "error"}: ${args.message}`);
101
+ this.status = args.status;
102
+ this.code = args.code;
103
+ this.requestId = args.requestId;
104
+ this.body = args.body;
42
105
  }
43
- // Send a message into the session's inbox (§4.2). Returns the created (or,
44
- // on an idempotent retry, the original) inbox row — clients key optimistic
45
- // echo chips on its id. `idempotencyKey` makes retries safe per
46
- // (session, key); a closed or non-interactive session rejects with a 409
47
- // (surface the server's curated copy).
48
- async sendSessionMessage(sessionId, message, options = {}) {
49
- return await this.fetchJson(
50
- `/sessions/${encodeURIComponent(sessionId)}/messages`,
51
- {
52
- method: "POST",
53
- body: {
54
- message,
55
- ...options.idempotencyKey != null ? { idempotency_key: options.idempotencyKey } : {}
106
+ };
107
+ var AuthenticationError = class extends APIError {
108
+ };
109
+ var ForbiddenError = class extends APIError {
110
+ };
111
+ var NotFoundError = class extends APIError {
112
+ };
113
+ var ConflictError = class extends APIError {
114
+ };
115
+ var UnprocessableError = class extends APIError {
116
+ };
117
+ var RateLimitError = class extends APIError {
118
+ };
119
+ var ServerError = class extends APIError {
120
+ };
121
+ var STATUS_CLASSES = {
122
+ 401: AuthenticationError,
123
+ 403: ForbiddenError,
124
+ 404: NotFoundError,
125
+ 409: ConflictError,
126
+ 422: UnprocessableError,
127
+ 429: RateLimitError
128
+ };
129
+ function apiErrorFor(status, body) {
130
+ let code = null;
131
+ let message = "";
132
+ let requestId = null;
133
+ if (body !== null && typeof body === "object") {
134
+ const error = body.error;
135
+ if (error !== null && typeof error === "object") {
136
+ const info = error;
137
+ if (typeof info.code === "string") code = info.code;
138
+ if (typeof info.message === "string") message = info.message;
139
+ if (typeof info.request_id === "string") requestId = info.request_id;
140
+ } else if ("detail" in body) {
141
+ message = String(body.detail);
142
+ }
143
+ }
144
+ if (!message) {
145
+ message = body == null ? "no response body" : String(body).slice(0, 500);
146
+ }
147
+ const cls = STATUS_CLASSES[status] ?? (status >= 500 ? ServerError : APIError);
148
+ return new cls({ status, code, message, requestId, body });
149
+ }
150
+
151
+ // src/core/transport.ts
152
+ var DEFAULT_BASE_URL = "https://api.ellipsis.dev";
153
+ var DEFAULT_TIMEOUT_MS = 6e4;
154
+ var RETRY_STATUSES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
155
+ var MAX_RETRIES = 2;
156
+ function queryScalar(value) {
157
+ if (value instanceof Date) return value.toISOString();
158
+ return String(value);
159
+ }
160
+ function buildQuery(params) {
161
+ const search = new URLSearchParams();
162
+ for (const [key, value] of Object.entries(params)) {
163
+ if (value == null) continue;
164
+ if (Array.isArray(value)) {
165
+ for (const item of value) search.append(key, queryScalar(item));
166
+ } else {
167
+ search.append(key, queryScalar(value));
168
+ }
169
+ }
170
+ const rendered = search.toString();
171
+ return rendered ? `?${rendered}` : "";
172
+ }
173
+ function buildBody(body) {
174
+ const out = {};
175
+ for (const [key, value] of Object.entries(body)) {
176
+ if (value == null) continue;
177
+ out[key] = value instanceof Date ? value.toISOString() : value;
178
+ }
179
+ return out;
180
+ }
181
+ function backoffMs(attempt, retryAfter) {
182
+ if (retryAfter != null) {
183
+ const seconds = Number(retryAfter);
184
+ if (!Number.isNaN(seconds)) return Math.max(0, seconds * 1e3);
185
+ }
186
+ return 2 ** attempt * 500 + Math.random() * 250;
187
+ }
188
+ var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
189
+ var Transport = class {
190
+ baseUrl;
191
+ headers;
192
+ timeoutMs;
193
+ maxRetries;
194
+ fetchImpl;
195
+ constructor(options) {
196
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
197
+ this.headers = { Authorization: `Bearer ${options.apiKey}` };
198
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
199
+ this.maxRetries = options.maxRetries ?? MAX_RETRIES;
200
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
201
+ }
202
+ async request(method, path, options = {}) {
203
+ const url = this.baseUrl + path + (options.query ?? "");
204
+ const init = { method, headers: { ...this.headers } };
205
+ if (options.body !== void 0) {
206
+ init.headers["Content-Type"] = "application/json";
207
+ init.body = JSON.stringify(options.body);
208
+ }
209
+ for (let attempt = 0; ; attempt++) {
210
+ let response;
211
+ try {
212
+ response = await this.fetchImpl(url, {
213
+ ...init,
214
+ signal: AbortSignal.timeout(this.timeoutMs)
215
+ });
216
+ } catch (error) {
217
+ if (attempt < this.maxRetries) {
218
+ await sleep2(backoffMs(attempt, null));
219
+ continue;
56
220
  }
221
+ throw new TransportError(String(error));
57
222
  }
223
+ if (RETRY_STATUSES.has(response.status) && attempt < this.maxRetries) {
224
+ await sleep2(backoffMs(attempt, response.headers.get("retry-after")));
225
+ continue;
226
+ }
227
+ if (response.status >= 400) {
228
+ let body = null;
229
+ try {
230
+ body = await response.json();
231
+ } catch {
232
+ body = await response.text().catch(() => null);
233
+ }
234
+ throw apiErrorFor(response.status, body);
235
+ }
236
+ if (response.status === 204) return void 0;
237
+ return await response.json();
238
+ }
239
+ }
240
+ };
241
+
242
+ // src/core/client.ts
243
+ var enc = encodeURIComponent;
244
+ var EllipsisAgentsConfigs = class {
245
+ constructor(transport) {
246
+ this.transport = transport;
247
+ }
248
+ transport;
249
+ /**
250
+ * Create Agent Config
251
+ *
252
+ * Create an agent config via pull request.
253
+ *
254
+ * The PR adds the config file to agents/ on the target
255
+ * repository.
256
+ *
257
+ * Accepts an inline config or a gallery template slug.
258
+ */
259
+ async create(options) {
260
+ const path = "/agents/configs";
261
+ const body = buildBody({ config: options.config, path: options.path, repository: options.repository, template_id: options.template_id });
262
+ return await this.transport.request("POST", path, { body });
263
+ }
264
+ /**
265
+ * Get Agent Config
266
+ *
267
+ * Return one saved agent config by id.
268
+ */
269
+ async get(config_id) {
270
+ const path = `/agents/configs/${enc(config_id)}`;
271
+ return await this.transport.request("GET", path);
272
+ }
273
+ /**
274
+ * List Agent Configs
275
+ *
276
+ * List saved agent configs.
277
+ */
278
+ async list() {
279
+ const path = "/agents/configs";
280
+ return await this.transport.request("GET", path);
281
+ }
282
+ };
283
+ var EllipsisAgentsDefaults = class {
284
+ constructor(transport) {
285
+ this.transport = transport;
286
+ }
287
+ transport;
288
+ /**
289
+ * Delete Agent Default
290
+ *
291
+ * Clear a default agent config.
292
+ *
293
+ * Addressed by rung: the account rung (repository omitted) or a
294
+ * repository rung.
295
+ *
296
+ * Refused for sandbox tokens.
297
+ */
298
+ async delete(options = {}) {
299
+ const path = "/agents/defaults";
300
+ const query = buildQuery({ repository: options.repository });
301
+ await this.transport.request("DELETE", path, { query });
302
+ }
303
+ /**
304
+ * List Agent Defaults
305
+ *
306
+ * List default agent configs.
307
+ *
308
+ * The account default and any per-repository defaults.
309
+ *
310
+ * Defaults are addressed by rung: the account rung is repository
311
+ * omitted, a repo rung is "owner/name" — never by row id.
312
+ */
313
+ async list() {
314
+ const path = "/agents/defaults";
315
+ return await this.transport.request("GET", path);
316
+ }
317
+ /**
318
+ * Put Agent Default
319
+ *
320
+ * Set a default agent config.
321
+ *
322
+ * Addressed by rung: the account rung (repository omitted) or a
323
+ * repository rung ("owner/name").
324
+ *
325
+ * Refused for sandbox tokens (potentially prompt-injected code
326
+ * must not repoint the account's ambient default); an unknown or
327
+ * foreign repository is a 404.
328
+ */
329
+ async set(options) {
330
+ const path = "/agents/defaults";
331
+ const body = buildBody({ config_id: options.config_id, repository: options.repository });
332
+ return await this.transport.request("PUT", path, { body });
333
+ }
334
+ };
335
+ var EllipsisAgentsTemplates = class {
336
+ constructor(transport) {
337
+ this.transport = transport;
338
+ }
339
+ transport;
340
+ /**
341
+ * Get Agent Template
342
+ *
343
+ * Return one built-in agent template by slug.
344
+ */
345
+ async get(template_id) {
346
+ const path = `/agents/templates/${enc(template_id)}`;
347
+ return await this.transport.request("GET", path);
348
+ }
349
+ /**
350
+ * List Agent Templates
351
+ *
352
+ * List the built-in starter agent templates.
353
+ *
354
+ * Behind auth but not account-scoped — the data is static
355
+ * product content shared by the dashboard, landing site, and CLI.
356
+ */
357
+ async list() {
358
+ const path = "/agents/templates";
359
+ return await this.transport.request("GET", path);
360
+ }
361
+ };
362
+ var EllipsisAgents = class {
363
+ constructor(transport) {
364
+ this.transport = transport;
365
+ this.configs = new EllipsisAgentsConfigs(transport);
366
+ this.defaults = new EllipsisAgentsDefaults(transport);
367
+ this.templates = new EllipsisAgentsTemplates(transport);
368
+ }
369
+ transport;
370
+ configs;
371
+ defaults;
372
+ templates;
373
+ };
374
+ var EllipsisAlerts = class {
375
+ constructor(transport) {
376
+ this.transport = transport;
377
+ }
378
+ transport;
379
+ /**
380
+ * Dismiss Alert
381
+ *
382
+ * Dismiss an open alert and return it.
383
+ *
384
+ * 409 when the alert is not open; 404 when it doesn't exist.
385
+ */
386
+ async dismiss(alert_id) {
387
+ const path = `/alerts/${enc(alert_id)}/dismiss`;
388
+ return await this.transport.request("POST", path);
389
+ }
390
+ /**
391
+ * Get Alert
392
+ *
393
+ * Return one alert by id.
394
+ *
395
+ * 404 if it doesn't exist or belongs to another organization.
396
+ */
397
+ async get(alert_id) {
398
+ const path = `/alerts/${enc(alert_id)}`;
399
+ return await this.transport.request("GET", path);
400
+ }
401
+ /**
402
+ * List Alerts
403
+ *
404
+ * List budget alerts for the organization, newest first.
405
+ *
406
+ * Optionally filtered by status and source.
407
+ *
408
+ * Available to all credential types, so an agent can check "is
409
+ * this org near budget?" mid-task.
410
+ */
411
+ async list(options = {}) {
412
+ const path = "/alerts";
413
+ const query = buildQuery({ status: options.status, source: options.source, limit: options.limit, cursor: options.cursor });
414
+ const response = await this.transport.request("GET", path, { query });
415
+ return new Page(
416
+ response,
417
+ "alerts",
418
+ (cursor) => this.list({ ...options, cursor })
58
419
  );
59
420
  }
60
- // Trigger a code review on a pull request, or on a pushed branch (the
61
- // platform finds-or-creates a draft PR for it). A review's `id` IS a session
62
- // id, so callers stream it with the stream client and then re-read it below
63
- // for the findings — which only exist once the review finalizes. A review
64
- // with nothing new to look at comes back with `status: 'skipped'`, not an
65
- // error.
66
- async createReview(request) {
67
- return await this.fetchJson("/reviews", {
68
- method: "POST",
69
- body: request
70
- });
421
+ };
422
+ var EllipsisAnalytics = class {
423
+ constructor(transport) {
424
+ this.transport = transport;
425
+ }
426
+ transport;
427
+ /**
428
+ * Get Analytics Metrics
429
+ *
430
+ * Return PR and review analytics metrics.
431
+ *
432
+ * The same data as the analytics dashboard.
433
+ *
434
+ * Windowing: pass explicit start/end, or days (default: the last
435
+ * 30 days). account_type in all|user|bot scopes the authors (bot =
436
+ * the apps/agents). Available to all credential types: the
437
+ * responses expose the organization's own GitHub PR/review activity,
438
+ * no secrets.
439
+ */
440
+ async metrics(options = {}) {
441
+ const path = "/analytics/metrics";
442
+ const query = buildQuery({ days: options.days, start: options.start, end: options.end, repo: options.repo, author: options.author, account_type: options.account_type, status: options.status });
443
+ return await this.transport.request("GET", path, { query });
444
+ }
445
+ /**
446
+ * Get Analytics Pull Requests
447
+ *
448
+ * Return pull-request analytics.
449
+ *
450
+ * Windowing: pass explicit start/end, or days (default: the last
451
+ * 30 days).
452
+ */
453
+ async pullRequests(options = {}) {
454
+ const path = "/analytics/pull-requests";
455
+ const query = buildQuery({ days: options.days, start: options.start, end: options.end, account_type: options.account_type, repository_id: options.repository_id, author_id: options.author_id, status: options.status });
456
+ return await this.transport.request("GET", path, { query });
457
+ }
458
+ /**
459
+ * Get Analytics Reviews
460
+ *
461
+ * Return code-review analytics.
462
+ *
463
+ * account_type scopes reviewers (bot|user|all) — e.g. "what apps
464
+ * review the most PRs?" is the reviewer facets here with
465
+ * account_type=bot. Windowing: pass explicit start/end, or days
466
+ * (default: the last 30 days).
467
+ */
468
+ async reviews(options = {}) {
469
+ const path = "/analytics/reviews";
470
+ const query = buildQuery({ days: options.days, start: options.start, end: options.end, repo: options.repo, author: options.author, account_type: options.account_type, review_state: options.review_state });
471
+ return await this.transport.request("GET", path, { query });
472
+ }
473
+ };
474
+ var EllipsisAuthCli = class {
475
+ constructor(transport) {
476
+ this.transport = transport;
477
+ }
478
+ transport;
479
+ /**
480
+ * Cli Auth Poll
481
+ *
482
+ * Poll a device-code auth flow for its token.
483
+ *
484
+ * Unauthenticated; the device code identifies the flow.
485
+ */
486
+ async poll(options) {
487
+ const path = "/auth/cli/poll";
488
+ const body = buildBody({ device_code: options.device_code });
489
+ return await this.transport.request("POST", path, { body });
490
+ }
491
+ /**
492
+ * Cli Auth Start
493
+ *
494
+ * Start a device-code auth flow for the CLI.
495
+ *
496
+ * Unauthenticated: the CLI has no credential yet — that's what
497
+ * it's obtaining. The human authorizes out-of-band in the
498
+ * dashboard.
499
+ */
500
+ async start() {
501
+ const path = "/auth/cli/start";
502
+ return await this.transport.request("POST", path);
503
+ }
504
+ };
505
+ var EllipsisAuth = class {
506
+ constructor(transport) {
507
+ this.transport = transport;
508
+ this.cli = new EllipsisAuthCli(transport);
71
509
  }
72
- // One review: scope, findings, parser counters, and the posting outcome.
73
- async getReview(reviewId) {
74
- return await this.fetchJson(
75
- `/reviews/${encodeURIComponent(reviewId)}`
510
+ transport;
511
+ cli;
512
+ };
513
+ var EllipsisFiles = class {
514
+ constructor(transport) {
515
+ this.transport = transport;
516
+ }
517
+ transport;
518
+ /**
519
+ * Create File
520
+ *
521
+ * Upload a file that outlives the sandbox.
522
+ *
523
+ * v1: PNG images only, base64 in the JSON body, 10 MiB cap. The
524
+ * primary caller is the in-sandbox `agent file upload` CLI (an
525
+ * agent persisting a screenshot to link on a PR), but all
526
+ * credential types work. Returns the org-membership-gated
527
+ * dashboard URL so callers never hard-code URL shapes.
528
+ */
529
+ async create(options) {
530
+ const path = "/files";
531
+ const body = buildBody({ content_type: options.content_type, data_b64: options.data_b64, filename: options.filename });
532
+ return await this.transport.request("POST", path, { body });
533
+ }
534
+ /**
535
+ * Delete File
536
+ *
537
+ * Delete a file.
538
+ *
539
+ * A foreign, missing, or already-deleted id is an
540
+ * indistinguishable 404, so ids can't be enumerated. Sandbox
541
+ * tokens get a 403 (a sandbox token sits next to
542
+ * potentially-untrusted code and must not be able to destroy the
543
+ * account's files — uploading is fine, destruction is not); API
544
+ * keys and user tokens may delete.
545
+ */
546
+ async delete(file_id) {
547
+ const path = `/files/${enc(file_id)}`;
548
+ await this.transport.request("DELETE", path);
549
+ }
550
+ /**
551
+ * Get File
552
+ *
553
+ * Return one file's metadata and download link.
554
+ *
555
+ * Includes the gated dashboard URL and a short-lived presigned
556
+ * `download_url`.
557
+ *
558
+ * To fetch the bytes locally, call this and then GET download_url
559
+ * immediately — the JSON API never carries the file. The CLI's
560
+ * `agent file get` wraps exactly that two-step.
561
+ */
562
+ async get(file_id) {
563
+ const path = `/files/${enc(file_id)}`;
564
+ return await this.transport.request("GET", path);
565
+ }
566
+ /**
567
+ * List Files
568
+ *
569
+ * List uploaded files, newest first.
570
+ *
571
+ * Metadata only — download URLs are minted per explicit GET, not
572
+ * per row. session_id scopes the list to one run's uploads.
573
+ */
574
+ async list(options = {}) {
575
+ const path = "/files";
576
+ const query = buildQuery({ session_id: options.session_id, limit: options.limit, cursor: options.cursor });
577
+ const response = await this.transport.request("GET", path, { query });
578
+ return new Page(
579
+ response,
580
+ "files",
581
+ (cursor) => this.list({ ...options, cursor })
582
+ );
583
+ }
584
+ };
585
+ var EllipsisIntegrationsGithub = class {
586
+ constructor(transport) {
587
+ this.transport = transport;
588
+ }
589
+ transport;
590
+ /**
591
+ * List Github Members
592
+ *
593
+ * List the GitHub organization roster.
594
+ *
595
+ * (Or the account itself for a personal workspace) — the universe of author_id values for
596
+ * /sessions and /sessions/search.
597
+ *
598
+ * Members carry their linked Slack identity when a Slack-GitHub
599
+ * link exists, and /slack/members carries the reverse link.
600
+ */
601
+ async members() {
602
+ const path = "/integrations/github/members";
603
+ return await this.transport.request("GET", path);
604
+ }
605
+ /**
606
+ * List Github Repositories
607
+ *
608
+ * List GitHub repositories connected to the installation.
609
+ */
610
+ async repos() {
611
+ const path = "/integrations/github/repos";
612
+ return await this.transport.request("GET", path);
613
+ }
614
+ };
615
+ var EllipsisIntegrationsLinear = class {
616
+ constructor(transport) {
617
+ this.transport = transport;
618
+ }
619
+ transport;
620
+ /**
621
+ * List Linear Teams
622
+ *
623
+ * List the teams of the connected Linear workspace.
624
+ */
625
+ async teams() {
626
+ const path = "/integrations/linear/teams";
627
+ return await this.transport.request("GET", path);
628
+ }
629
+ };
630
+ var EllipsisIntegrationsSentry = class {
631
+ constructor(transport) {
632
+ this.transport = transport;
633
+ }
634
+ transport;
635
+ /**
636
+ * List Sentry Organizations
637
+ *
638
+ * List the connected Sentry organizations.
639
+ */
640
+ async organizations() {
641
+ const path = "/integrations/sentry/organizations";
642
+ return await this.transport.request("GET", path);
643
+ }
644
+ };
645
+ var EllipsisIntegrationsSlack = class {
646
+ constructor(transport) {
647
+ this.transport = transport;
648
+ }
649
+ transport;
650
+ /**
651
+ * List Slack Channels
652
+ *
653
+ * List the channels of the connected Slack workspace, fetched
654
+ * live from the Slack API.
655
+ */
656
+ async channels() {
657
+ const path = "/integrations/slack/channels";
658
+ return await this.transport.request("GET", path);
659
+ }
660
+ /**
661
+ * List Slack Members
662
+ *
663
+ * List the members of the connected Slack workspace, fetched
664
+ * live from the Slack API.
665
+ */
666
+ async members() {
667
+ const path = "/integrations/slack/members";
668
+ return await this.transport.request("GET", path);
669
+ }
670
+ };
671
+ var EllipsisIntegrations = class {
672
+ constructor(transport) {
673
+ this.transport = transport;
674
+ this.github = new EllipsisIntegrationsGithub(transport);
675
+ this.linear = new EllipsisIntegrationsLinear(transport);
676
+ this.sentry = new EllipsisIntegrationsSentry(transport);
677
+ this.slack = new EllipsisIntegrationsSlack(transport);
678
+ }
679
+ transport;
680
+ github;
681
+ linear;
682
+ sentry;
683
+ slack;
684
+ /**
685
+ * Get Integrations
686
+ *
687
+ * List connected integrations.
688
+ *
689
+ * A read-only view, so an agent authoring another agent's
690
+ * config can learn which repositories, channels, teams, and
691
+ * organizations it may name before POST /agents/configs.
692
+ *
693
+ * Available to all credential types including sandbox tokens: the
694
+ * responses never include OAuth tokens or any other secret.
695
+ */
696
+ async list() {
697
+ const path = "/integrations";
698
+ return await this.transport.request("GET", path);
699
+ }
700
+ };
701
+ var EllipsisMemories = class {
702
+ constructor(transport) {
703
+ this.transport = transport;
704
+ }
705
+ transport;
706
+ /**
707
+ * Create Memory
708
+ *
709
+ * Save a memory for future agent sessions.
710
+ *
711
+ * Memories are durable lessons shared across the whole
712
+ * organization — conventions, past decisions, known gotchas that an
713
+ * agent cannot derive from the repository itself. One concise fact
714
+ * per memory; the description is what a reader sees in the index.
715
+ *
716
+ * Creating never overwrites: a path that already exists is a 409, so
717
+ * correcting an existing memory is an explicit edit.
718
+ */
719
+ async create(options) {
720
+ const path = "/memories";
721
+ const body = buildBody({ content: options.content, description: options.description, path: options.path });
722
+ return await this.transport.request("POST", path, { body });
723
+ }
724
+ /**
725
+ * Delete Memory
726
+ *
727
+ * Delete a memory that is no longer true.
728
+ *
729
+ * Available to every credential type, agents included — a wrong
730
+ * memory is worse than a missing one. The deleted content is kept in
731
+ * the memory's history for forensics, not for an undo API.
732
+ */
733
+ async delete(memory_id, options = {}) {
734
+ const path = `/memories/${enc(memory_id)}`;
735
+ const query = buildQuery({ if_sha256: options.if_sha256 });
736
+ await this.transport.request("DELETE", path, { query });
737
+ }
738
+ /**
739
+ * Edit Memory
740
+ *
741
+ * Correct an existing memory in place.
742
+ *
743
+ * Pass description, content, or both. This never creates a memory (an
744
+ * unknown id is a 404) and never moves one — the path is immutable, so
745
+ * relocating a memory is a delete plus a create.
746
+ *
747
+ * Pass `if_sha256` (from a previous read) to make a concurrent write
748
+ * a 409 instead of silently overwriting it.
749
+ */
750
+ async edit(memory_id, options = {}) {
751
+ const path = `/memories/${enc(memory_id)}`;
752
+ const body = buildBody({ content: options.content, description: options.description, if_sha256: options.if_sha256 });
753
+ return await this.transport.request("PUT", path, { body });
754
+ }
755
+ /**
756
+ * Get Memory
757
+ *
758
+ * Read one memory's full content.
759
+ *
760
+ * The id comes from the index returned by listing memories.
761
+ */
762
+ async get(memory_id) {
763
+ const path = `/memories/${enc(memory_id)}`;
764
+ return await this.transport.request("GET", path);
765
+ }
766
+ /**
767
+ * List Memories
768
+ *
769
+ * List the organization's memories as a readable index.
770
+ *
771
+ * Returns the memories rendered as a markdown index (one line per
772
+ * memory, carrying its id, path, and description) plus the same
773
+ * entries structured, each with every field except `content`. Read
774
+ * the index, then fetch the ids whose content you want.
775
+ */
776
+ async list(options = {}) {
777
+ const path = "/memories";
778
+ const query = buildQuery({ prefix: options.prefix });
779
+ return await this.transport.request("GET", path, { query });
780
+ }
781
+ };
782
+ var EllipsisModels = class {
783
+ constructor(transport) {
784
+ this.transport = transport;
785
+ }
786
+ transport;
787
+ /**
788
+ * List Supported Models
789
+ *
790
+ * List selectable agent models.
791
+ *
792
+ * Most expensive first, with the platform default flagged.
793
+ *
794
+ * Reads the same registry as the dashboard's rate table, so a
795
+ * client's model picker can never drift from what's offered.
796
+ * Behind auth but not account-scoped — the selectable set is
797
+ * global.
798
+ */
799
+ async list() {
800
+ const path = "/models";
801
+ return await this.transport.request("GET", path);
802
+ }
803
+ };
804
+ var EllipsisReviews = class {
805
+ constructor(transport) {
806
+ this.transport = transport;
807
+ }
808
+ transport;
809
+ /**
810
+ * Create Review
811
+ *
812
+ * Run a code review on demand.
813
+ *
814
+ * Reviews an existing PR or a pushed branch (for a branch, the
815
+ * platform finds or creates a draft PR for it, since a code
816
+ * review is structurally about a PR).
817
+ *
818
+ * A review is one pipeline run over one commit range, with one
819
+ * agent session per stage: `review.id` is the run id, and
820
+ * `stages[].session_id` is where a client streams
821
+ * (/sessions/{id}/stream on a stage session), since the review
822
+ * itself is a pipeline rather than one process.
823
+ */
824
+ async create(options) {
825
+ const path = "/reviews";
826
+ const body = buildBody({ owner: options.owner, post: options.post, pull_request_number: options.pull_request_number, repo: options.repo, scope: options.scope });
827
+ return await this.transport.request("POST", path, { body });
828
+ }
829
+ /**
830
+ * Get Review
831
+ *
832
+ * Return one review with findings and outcome.
833
+ *
834
+ * Includes its scope, the per-stage sessions, the parsed findings,
835
+ * the finding counters, and the posting outcome.
836
+ *
837
+ * Findings only exist after a stage session finalizes — a running
838
+ * review honestly reports [] / null, which is why a client
839
+ * streams a stage session and then re-fetches the review.
840
+ */
841
+ async get(review_id) {
842
+ const path = `/reviews/${enc(review_id)}`;
843
+ return await this.transport.request("GET", path);
844
+ }
845
+ /**
846
+ * List Reviews
847
+ *
848
+ * List code reviews, newest first.
849
+ *
850
+ * `findings` is omitted (counters only).
851
+ *
852
+ * Webhook-created reviews appear here too, so this answers "show
853
+ * me every review on this PR".
854
+ */
855
+ async list(options = {}) {
856
+ const path = "/reviews";
857
+ const query = buildQuery({ owner: options.owner, repo: options.repo, pull_request_number: options.pull_request_number, status: options.status, limit: options.limit, cursor: options.cursor });
858
+ const response = await this.transport.request("GET", path, { query });
859
+ return new Page(
860
+ response,
861
+ "reviews",
862
+ (cursor) => this.list({ ...options, cursor })
76
863
  );
77
864
  }
78
- // Reviews newest first, `findings` omitted (counters only). Includes
79
- // webhook-triggered reviews, so this is the full review history of a PR —
80
- // the chain the incremental watermark walks.
81
- async listReviews(options = {}) {
82
- const params = new URLSearchParams();
83
- if (options.owner != null) params.set("owner", options.owner);
84
- if (options.repo != null) params.set("repo", options.repo);
85
- if (options.pullRequestNumber != null)
86
- params.set("pull_request_number", String(options.pullRequestNumber));
87
- if (options.status != null) params.set("status", options.status);
88
- if (options.limit != null) params.set("limit", String(options.limit));
89
- const query = params.toString();
90
- return await this.fetchJson(
91
- "/reviews" + (query ? `?${query}` : "")
865
+ };
866
+ var EllipsisSecrets = class {
867
+ constructor(transport) {
868
+ this.transport = transport;
869
+ }
870
+ transport;
871
+ /**
872
+ * Delete Secret
873
+ *
874
+ * Delete a secret by name.
875
+ *
876
+ * Refused for sandbox tokens; mutations require an API key or
877
+ * user token.
878
+ */
879
+ async delete(name) {
880
+ const path = `/secrets/${enc(name)}`;
881
+ await this.transport.request("DELETE", path);
882
+ }
883
+ /**
884
+ * List Secrets
885
+ *
886
+ * List secrets.
887
+ *
888
+ * Values are write-only: the response carries only names and
889
+ * timestamps, never the stored value.
890
+ */
891
+ async list() {
892
+ const path = "/secrets";
893
+ return await this.transport.request("GET", path);
894
+ }
895
+ /**
896
+ * Put Secrets
897
+ *
898
+ * Upsert secrets.
899
+ *
900
+ * Returns only the secrets this call wrote, names and timestamps
901
+ * only — values are write-only. Refused for sandbox tokens (held by
902
+ * potentially-untrusted in-sandbox code); mutations require an
903
+ * API key or user token.
904
+ */
905
+ async set(options) {
906
+ const path = "/secrets";
907
+ const body = buildBody({ secrets: options.secrets });
908
+ return await this.transport.request("PUT", path, { body });
909
+ }
910
+ };
911
+ var EllipsisSessions = class {
912
+ constructor(transport) {
913
+ this.transport = transport;
914
+ }
915
+ transport;
916
+ /**
917
+ * Get Agent Session Executions
918
+ *
919
+ * Return the session's executions and launch context.
920
+ *
921
+ * Most notably system_prompt_append, the text Ellipsis appends to
922
+ * Claude Code's default system prompt (platform section + response
923
+ * instructions + the config's own instructions).
924
+ *
925
+ * Per execution because each cold wake persists its own launch
926
+ * config (a config edit or replay override between wakes changes
927
+ * it).
928
+ */
929
+ async executions(session_id) {
930
+ const path = `/sessions/${enc(session_id)}/executions`;
931
+ return await this.transport.request("GET", path);
932
+ }
933
+ /**
934
+ * Get Agent Session
935
+ *
936
+ * Return one session.
937
+ *
938
+ * The public wire shape — the same shape the stream's session
939
+ * frames carry, with attributed_user and stopped_by_user resolved
940
+ * at read time.
941
+ *
942
+ * The heavy near-static blobs (config snapshot, input/output)
943
+ * live on the detail endpoints, not here.
944
+ */
945
+ async get(session_id) {
946
+ const path = `/sessions/${enc(session_id)}`;
947
+ return await this.transport.request("GET", path);
948
+ }
949
+ /**
950
+ * Get Agent Session Ide
951
+ *
952
+ * Return the IDE link for the session's sandbox.
953
+ *
954
+ * The membership-gated dashboard page, which performs the sandbox
955
+ * proxy handoff itself.
956
+ *
957
+ * No credential is minted here — the URL grants nothing by
958
+ * possession. 409 when the sandbox isn't running (send the
959
+ * session a message to wake it first). Backs the `agent session
960
+ * ide` CLI verb.
961
+ */
962
+ async ide(session_id) {
963
+ const path = `/sessions/${enc(session_id)}/ide`;
964
+ return await this.transport.request("GET", path);
965
+ }
966
+ /**
967
+ * Ingest Agent Session Transcript
968
+ *
969
+ * Append transcript lines to a session's record.
970
+ *
971
+ * The in-sandbox tailer for interactive sessions.
972
+ *
973
+ * A sandbox token may only push its own session's transcript.
974
+ * Idempotent by transcript line uuid; tolerant of malformed lines.
975
+ */
976
+ async ingestTranscript(session_id, options) {
977
+ const path = `/sessions/${enc(session_id)}/transcript`;
978
+ const body = buildBody({ lines: options.lines, offset: options.offset });
979
+ return await this.transport.request("POST", path, { body });
980
+ }
981
+ /**
982
+ * List Agent Sessions
983
+ *
984
+ * List cloud agent sessions, newest first.
985
+ *
986
+ * Optionally filtered by config, source, time window, attributed
987
+ * author, repository, and whether the session is still going.
988
+ */
989
+ async list(options = {}) {
990
+ const path = "/sessions";
991
+ const query = buildQuery({ config_id: options.config_id, source: options.source, days: options.days, start: options.start, end: options.end, limit: options.limit, cursor: options.cursor, author_id: options.author_id, repo: options.repo, unfinished: options.unfinished });
992
+ const response = await this.transport.request("GET", path, { query });
993
+ return new Page(
994
+ response,
995
+ "sessions",
996
+ (cursor) => this.list({ ...options, cursor })
92
997
  );
93
998
  }
999
+ /**
1000
+ * Get Agent Session Log
1001
+ *
1002
+ * Download the complete session log.
1003
+ *
1004
+ * The complete, ordered history of the session (agent output, tool calls, thinking,
1005
+ * lifecycle events, sandbox output, message events) as an ordered
1006
+ * manifest of presigned segment URLs.
1007
+ *
1008
+ * Segments are gzip members: download in order and concatenate
1009
+ * for one file. The JSON API never carries the bytes (same
1010
+ * two-step as files). /records is the paged live view of the
1011
+ * same data.
1012
+ */
1013
+ async log(session_id) {
1014
+ const path = `/sessions/${enc(session_id)}/log`;
1015
+ return await this.transport.request("GET", path);
1016
+ }
1017
+ /**
1018
+ * Get Agent Session Port
1019
+ *
1020
+ * Return a preview link for a sandbox port.
1021
+ *
1022
+ * For a dev server running in the session's live sandbox: the
1023
+ * same dashboard page as the IDE, deep-linked to the port.
1024
+ *
1025
+ * Backs `agent session port`.
1026
+ */
1027
+ async port(session_id, port) {
1028
+ const path = `/sessions/${enc(session_id)}/ports/${enc(String(port))}`;
1029
+ return await this.transport.request("GET", path);
1030
+ }
1031
+ /**
1032
+ * Get Agent Session Records
1033
+ *
1034
+ * Return the session's stored transcript records, oldest first.
1035
+ *
1036
+ * Pagination is opt-in (cursor/limit); a bare call returns the
1037
+ * full retained transcript. Available to sandbox tokens too: an
1038
+ * agent answering "did X look into Y?" needs to read the session
1039
+ * it found via /sessions/search, and any org member sees the same
1040
+ * transcript in the dashboard (laptop transcripts are redacted
1041
+ * client-side before they are ever uploaded).
1042
+ */
1043
+ async records(session_id, options = {}) {
1044
+ const path = `/sessions/${enc(session_id)}/records`;
1045
+ const query = buildQuery({ cursor: options.cursor, limit: options.limit });
1046
+ const response = await this.transport.request("GET", path, { query });
1047
+ return new Page(
1048
+ response,
1049
+ "records",
1050
+ (cursor) => this.records(session_id, { ...options, cursor })
1051
+ );
1052
+ }
1053
+ /**
1054
+ * Replay Agent Session
1055
+ *
1056
+ * Replay a session as a new session.
1057
+ *
1058
+ * Reuses the original config snapshot unless config_id is given,
1059
+ * and accepts the same config_override as session start — e.g.
1060
+ * {"claude": {"model": ...}} to replay the same input on a
1061
+ * different model.
1062
+ */
1063
+ async replay(session_id, options = {}) {
1064
+ const path = `/sessions/${enc(session_id)}/replay`;
1065
+ const body = buildBody({ config_id: options.config_id, config_override: options.config_override, config_override_yaml: options.config_override_yaml, prompt: options.prompt });
1066
+ return await this.transport.request("POST", path, { body });
1067
+ }
1068
+ /**
1069
+ * Search Sessions
1070
+ *
1071
+ * Search sessions over steps, recaps, and pull requests.
1072
+ *
1073
+ * Grouped by session, over everything a session left behind: step text, recap text, created
1074
+ * PRs, and recap-embedding similarity.
1075
+ *
1076
+ * This is how an agent answers "did Tony look into X?": resolve
1077
+ * the author via /github/members, search, then read the winning
1078
+ * session via /sessions/{id} (recap) and /sessions/{id}/records
1079
+ * (transcript).
1080
+ */
1081
+ async search(options = {}) {
1082
+ const path = "/sessions/search";
1083
+ const query = buildQuery({ q: options.q, scope: options.scope, source: options.source, author_id: options.author_id, config_id: options.config_id, session_ids: options.session_ids, repo: options.repo, status: options.status, start: options.start, end: options.end, limit: options.limit });
1084
+ return await this.transport.request("GET", path, { query });
1085
+ }
1086
+ /**
1087
+ * Send Agent Session Message
1088
+ *
1089
+ * Send a message to a session.
1090
+ *
1091
+ * 409 for react/cron, non-interactive, and closed sessions.
1092
+ * Returns the created message so the caller can track it by id; a
1093
+ * retried send with the same idempotency_key returns the original
1094
+ * message.
1095
+ */
1096
+ async sendMessage(session_id, options) {
1097
+ const path = `/sessions/${enc(session_id)}/messages`;
1098
+ const body = buildBody({ idempotency_key: options.idempotency_key, message: options.message });
1099
+ return await this.transport.request("POST", path, { body });
1100
+ }
1101
+ /**
1102
+ * Start Agent Session
1103
+ *
1104
+ * Start a cloud agent session.
1105
+ *
1106
+ * Provide at most one of config_id, config, or template_id; with
1107
+ * none, the account's default-config ladder resolves the config.
1108
+ * 400 when idle_start is combined with prompt or handoff, or when
1109
+ * a handoff is combined with any config source or override.
1110
+ */
1111
+ async start(options = {}) {
1112
+ const path = "/sessions";
1113
+ const body = buildBody({ config: options.config, config_id: options.config_id, config_override: options.config_override, config_override_yaml: options.config_override_yaml, force_rebuild: options.force_rebuild, handoff: options.handoff, idle_start: options.idle_start, metadata: options.metadata, prompt: options.prompt, repository: options.repository, template_id: options.template_id });
1114
+ return await this.transport.request("POST", path, { body });
1115
+ }
1116
+ /**
1117
+ * Stop Agent Session
1118
+ *
1119
+ * Stop an in-flight session and return it.
1120
+ *
1121
+ * stopped_by is recorded only when the caller's credential maps
1122
+ * to a GitHub account.
1123
+ */
1124
+ async stop(session_id) {
1125
+ const path = `/sessions/${enc(session_id)}/stop`;
1126
+ return await this.transport.request("POST", path);
1127
+ }
1128
+ /**
1129
+ * Sync Agent Session
1130
+ *
1131
+ * Sync a local Claude Code session to Ellipsis.
1132
+ *
1133
+ * Requires a user token (device-flow `agent login`): the user is
1134
+ * part of the sync's idempotency key, so API keys and sandbox
1135
+ * tokens are rejected with a 403 rather than silently attributed.
1136
+ */
1137
+ async sync(options) {
1138
+ const path = "/sessions/sync";
1139
+ const body = buildBody({ cc_session_id: options.cc_session_id, cwd: options.cwd, git_branch: options.git_branch, reason: options.reason, repo: options.repo, transcript_gzip_b64: options.transcript_gzip_b64 });
1140
+ return await this.transport.request("POST", path, { body });
1141
+ }
1142
+ // ---- lifecycle sugar (hand-written layer over the generated core) ----
1143
+ /** Start a session and return a handle over it. */
1144
+ async run(options) {
1145
+ const response = await this.start(options);
1146
+ return new SessionHandle(this, response.session);
1147
+ }
1148
+ /** A handle over an existing session. */
1149
+ async handle(sessionId) {
1150
+ const response = await this.get(sessionId);
1151
+ return new SessionHandle(this, response.session);
1152
+ }
1153
+ };
1154
+ var Ellipsis = class {
1155
+ transport;
1156
+ agents;
1157
+ alerts;
1158
+ analytics;
1159
+ auth;
1160
+ files;
1161
+ integrations;
1162
+ memories;
1163
+ models;
1164
+ reviews;
1165
+ secrets;
1166
+ sessions;
1167
+ constructor(options) {
1168
+ this.transport = new Transport(options);
1169
+ this.agents = new EllipsisAgents(this.transport);
1170
+ this.alerts = new EllipsisAlerts(this.transport);
1171
+ this.analytics = new EllipsisAnalytics(this.transport);
1172
+ this.auth = new EllipsisAuth(this.transport);
1173
+ this.files = new EllipsisFiles(this.transport);
1174
+ this.integrations = new EllipsisIntegrations(this.transport);
1175
+ this.memories = new EllipsisMemories(this.transport);
1176
+ this.models = new EllipsisModels(this.transport);
1177
+ this.reviews = new EllipsisReviews(this.transport);
1178
+ this.secrets = new EllipsisSecrets(this.transport);
1179
+ this.sessions = new EllipsisSessions(this.transport);
1180
+ }
1181
+ /**
1182
+ * Get Budget
1183
+ *
1184
+ * Return the caller's current budget summary.
1185
+ */
1186
+ async budget() {
1187
+ const path = "/budget";
1188
+ return await this.transport.request("GET", path);
1189
+ }
1190
+ /**
1191
+ * Whoami
1192
+ *
1193
+ * Return the identity behind the caller's credential.
1194
+ */
1195
+ async me() {
1196
+ const path = "/me";
1197
+ return await this.transport.request("GET", path);
1198
+ }
1199
+ /**
1200
+ * Get Usage Endpoint
1201
+ *
1202
+ * Return the caller's usage dashboard data.
1203
+ */
1204
+ async usage() {
1205
+ const path = "/usage";
1206
+ return await this.transport.request("GET", path);
1207
+ }
94
1208
  };
95
1209
  export {
96
- EllipsisClient
1210
+ APIError,
1211
+ AuthenticationError,
1212
+ ConflictError,
1213
+ DEFAULT_BASE_URL,
1214
+ Ellipsis,
1215
+ EllipsisError,
1216
+ ForbiddenError,
1217
+ NotFoundError,
1218
+ Page,
1219
+ RateLimitError,
1220
+ ServerError,
1221
+ SessionHandle,
1222
+ TERMINAL_STATUSES,
1223
+ Transport,
1224
+ TransportError,
1225
+ UnprocessableError,
1226
+ isSettled
97
1227
  };