@llmsafespaces/sdk 0.22.0 → 0.23.0

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.cjs CHANGED
@@ -205,6 +205,52 @@ var LLMSafeSpaces = class {
205
205
  if (text === "") return void 0;
206
206
  return JSON.parse(text);
207
207
  }
208
+ /**
209
+ * Internal: like {@link request}, but also returns the response headers
210
+ * (e.g. pagination cursors). Body decoding follows the same contract.
211
+ */
212
+ async requestWithHeaders(method, path, body, timeout) {
213
+ const url = `${this.baseUrl}/api/v1${path}`;
214
+ const headers = { "Content-Type": "application/json" };
215
+ if (this.apiKey) {
216
+ headers["Authorization"] = `Bearer ${this.apiKey}`;
217
+ } else if (this.token) {
218
+ headers["Authorization"] = `Bearer ${this.token}`;
219
+ }
220
+ const controller = new AbortController();
221
+ const timer = setTimeout(() => controller.abort(), timeout ?? this.timeout);
222
+ let res;
223
+ try {
224
+ res = await this.fetchFn(url, {
225
+ method,
226
+ headers,
227
+ body: body ? JSON.stringify(body) : void 0,
228
+ signal: controller.signal
229
+ });
230
+ } catch (e) {
231
+ clearTimeout(timer);
232
+ if (e instanceof Error && e.name === "AbortError") {
233
+ throw new TimeoutError();
234
+ }
235
+ throw e;
236
+ }
237
+ clearTimeout(timer);
238
+ if (!res.ok) {
239
+ const errBody = await res.json().catch(() => ({ error: res.statusText }));
240
+ const msg = errBody.error ?? res.statusText;
241
+ if (res.status === 401 || res.status === 403) throw new AuthError(msg, res.status);
242
+ if (res.status === 404) throw new NotFoundError(msg);
243
+ throw new LLMSafeSpacesError(msg, res.status);
244
+ }
245
+ let data;
246
+ if (res.status === 204) {
247
+ data = void 0;
248
+ } else {
249
+ const text = await res.text();
250
+ data = text === "" ? void 0 : JSON.parse(text);
251
+ }
252
+ return { data, headers: res.headers };
253
+ }
208
254
  async login() {
209
255
  if (!this.credentials) throw new AuthError("No credentials configured");
210
256
  this.loggingIn = true;
@@ -327,6 +373,20 @@ var SessionsAPI = class {
327
373
  getHistory(workspaceId, sessionId) {
328
374
  return this.client.request("GET", `/workspaces/${workspaceId}/sessions/${sessionId}/message`);
329
375
  }
376
+ /**
377
+ * Returns one page of session history with cursor pagination.
378
+ * nextCursor is "" when the beginning of the session was reached
379
+ * (no X-Next-Cursor response header).
380
+ */
381
+ async getHistoryPage(workspaceId, sessionId, opts) {
382
+ const q = new URLSearchParams();
383
+ if (opts?.limit && opts.limit > 0) q.set("limit", String(opts.limit));
384
+ if (opts?.before) q.set("before", opts.before);
385
+ const qs = q.toString();
386
+ const path = `/workspaces/${workspaceId}/sessions/${sessionId}/message${qs ? `?${qs}` : ""}`;
387
+ const { data, headers } = await this.client.requestWithHeaders("GET", path);
388
+ return { messages: data ?? [], nextCursor: headers.get("X-Next-Cursor") ?? "" };
389
+ }
330
390
  abort(workspaceId, sessionId) {
331
391
  return this.client.request("POST", `/workspaces/${workspaceId}/sessions/${sessionId}/abort`);
332
392
  }
package/dist/index.d.cts CHANGED
@@ -322,6 +322,14 @@ declare class LLMSafeSpaces {
322
322
  constructor(options: ClientOptions);
323
323
  /** Internal: make an authenticated request. */
324
324
  request<T>(method: string, path: string, body?: unknown, timeout?: number): Promise<T>;
325
+ /**
326
+ * Internal: like {@link request}, but also returns the response headers
327
+ * (e.g. pagination cursors). Body decoding follows the same contract.
328
+ */
329
+ requestWithHeaders<T>(method: string, path: string, body?: unknown, timeout?: number): Promise<{
330
+ data: T;
331
+ headers: Headers;
332
+ }>;
325
333
  private login;
326
334
  }
327
335
  declare class WorkspacesAPI {
@@ -378,6 +386,18 @@ declare class SessionsAPI {
378
386
  sendMessage(workspaceId: string, sessionId: string, content: string): Promise<Message>;
379
387
  /** Returns the session transcript in contract shape. */
380
388
  getHistory(workspaceId: string, sessionId: string): Promise<Message[]>;
389
+ /**
390
+ * Returns one page of session history with cursor pagination.
391
+ * nextCursor is "" when the beginning of the session was reached
392
+ * (no X-Next-Cursor response header).
393
+ */
394
+ getHistoryPage(workspaceId: string, sessionId: string, opts?: {
395
+ limit?: number;
396
+ before?: string;
397
+ }): Promise<{
398
+ messages: Message[];
399
+ nextCursor: string;
400
+ }>;
381
401
  abort(workspaceId: string, sessionId: string): Promise<void>;
382
402
  get(workspaceId: string, sessionId: string): Promise<Record<string, unknown>>;
383
403
  sendPromptAsync(workspaceId: string, sessionId: string, message: string): Promise<void>;
package/dist/index.d.ts CHANGED
@@ -322,6 +322,14 @@ declare class LLMSafeSpaces {
322
322
  constructor(options: ClientOptions);
323
323
  /** Internal: make an authenticated request. */
324
324
  request<T>(method: string, path: string, body?: unknown, timeout?: number): Promise<T>;
325
+ /**
326
+ * Internal: like {@link request}, but also returns the response headers
327
+ * (e.g. pagination cursors). Body decoding follows the same contract.
328
+ */
329
+ requestWithHeaders<T>(method: string, path: string, body?: unknown, timeout?: number): Promise<{
330
+ data: T;
331
+ headers: Headers;
332
+ }>;
325
333
  private login;
326
334
  }
327
335
  declare class WorkspacesAPI {
@@ -378,6 +386,18 @@ declare class SessionsAPI {
378
386
  sendMessage(workspaceId: string, sessionId: string, content: string): Promise<Message>;
379
387
  /** Returns the session transcript in contract shape. */
380
388
  getHistory(workspaceId: string, sessionId: string): Promise<Message[]>;
389
+ /**
390
+ * Returns one page of session history with cursor pagination.
391
+ * nextCursor is "" when the beginning of the session was reached
392
+ * (no X-Next-Cursor response header).
393
+ */
394
+ getHistoryPage(workspaceId: string, sessionId: string, opts?: {
395
+ limit?: number;
396
+ before?: string;
397
+ }): Promise<{
398
+ messages: Message[];
399
+ nextCursor: string;
400
+ }>;
381
401
  abort(workspaceId: string, sessionId: string): Promise<void>;
382
402
  get(workspaceId: string, sessionId: string): Promise<Record<string, unknown>>;
383
403
  sendPromptAsync(workspaceId: string, sessionId: string, message: string): Promise<void>;
package/dist/index.js CHANGED
@@ -171,6 +171,52 @@ var LLMSafeSpaces = class {
171
171
  if (text === "") return void 0;
172
172
  return JSON.parse(text);
173
173
  }
174
+ /**
175
+ * Internal: like {@link request}, but also returns the response headers
176
+ * (e.g. pagination cursors). Body decoding follows the same contract.
177
+ */
178
+ async requestWithHeaders(method, path, body, timeout) {
179
+ const url = `${this.baseUrl}/api/v1${path}`;
180
+ const headers = { "Content-Type": "application/json" };
181
+ if (this.apiKey) {
182
+ headers["Authorization"] = `Bearer ${this.apiKey}`;
183
+ } else if (this.token) {
184
+ headers["Authorization"] = `Bearer ${this.token}`;
185
+ }
186
+ const controller = new AbortController();
187
+ const timer = setTimeout(() => controller.abort(), timeout ?? this.timeout);
188
+ let res;
189
+ try {
190
+ res = await this.fetchFn(url, {
191
+ method,
192
+ headers,
193
+ body: body ? JSON.stringify(body) : void 0,
194
+ signal: controller.signal
195
+ });
196
+ } catch (e) {
197
+ clearTimeout(timer);
198
+ if (e instanceof Error && e.name === "AbortError") {
199
+ throw new TimeoutError();
200
+ }
201
+ throw e;
202
+ }
203
+ clearTimeout(timer);
204
+ if (!res.ok) {
205
+ const errBody = await res.json().catch(() => ({ error: res.statusText }));
206
+ const msg = errBody.error ?? res.statusText;
207
+ if (res.status === 401 || res.status === 403) throw new AuthError(msg, res.status);
208
+ if (res.status === 404) throw new NotFoundError(msg);
209
+ throw new LLMSafeSpacesError(msg, res.status);
210
+ }
211
+ let data;
212
+ if (res.status === 204) {
213
+ data = void 0;
214
+ } else {
215
+ const text = await res.text();
216
+ data = text === "" ? void 0 : JSON.parse(text);
217
+ }
218
+ return { data, headers: res.headers };
219
+ }
174
220
  async login() {
175
221
  if (!this.credentials) throw new AuthError("No credentials configured");
176
222
  this.loggingIn = true;
@@ -293,6 +339,20 @@ var SessionsAPI = class {
293
339
  getHistory(workspaceId, sessionId) {
294
340
  return this.client.request("GET", `/workspaces/${workspaceId}/sessions/${sessionId}/message`);
295
341
  }
342
+ /**
343
+ * Returns one page of session history with cursor pagination.
344
+ * nextCursor is "" when the beginning of the session was reached
345
+ * (no X-Next-Cursor response header).
346
+ */
347
+ async getHistoryPage(workspaceId, sessionId, opts) {
348
+ const q = new URLSearchParams();
349
+ if (opts?.limit && opts.limit > 0) q.set("limit", String(opts.limit));
350
+ if (opts?.before) q.set("before", opts.before);
351
+ const qs = q.toString();
352
+ const path = `/workspaces/${workspaceId}/sessions/${sessionId}/message${qs ? `?${qs}` : ""}`;
353
+ const { data, headers } = await this.client.requestWithHeaders("GET", path);
354
+ return { messages: data ?? [], nextCursor: headers.get("X-Next-Cursor") ?? "" };
355
+ }
296
356
  abort(workspaceId, sessionId) {
297
357
  return this.client.request("POST", `/workspaces/${workspaceId}/sessions/${sessionId}/abort`);
298
358
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llmsafespaces/sdk",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "TypeScript SDK for LLMSafeSpaces API",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",