langsmith 0.5.19 → 0.5.20

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.
@@ -10,6 +10,27 @@ const async_caller_js_1 = require("../../utils/async_caller.cjs");
10
10
  const sandbox_js_1 = require("./sandbox.cjs");
11
11
  const errors_js_1 = require("./errors.cjs");
12
12
  const helpers_js_1 = require("./helpers.cjs");
13
+ /**
14
+ * Sleep that can be interrupted by an AbortSignal.
15
+ * Resolves after `ms` milliseconds or rejects immediately if the signal fires.
16
+ */
17
+ function sleepWithSignal(ms, signal) {
18
+ if (!signal) {
19
+ return new Promise((resolve) => setTimeout(resolve, ms));
20
+ }
21
+ signal.throwIfAborted();
22
+ return new Promise((resolve, reject) => {
23
+ const timer = setTimeout(() => {
24
+ signal.removeEventListener("abort", onAbort);
25
+ resolve();
26
+ }, ms);
27
+ function onAbort() {
28
+ clearTimeout(timer);
29
+ reject(signal.reason);
30
+ }
31
+ signal.addEventListener("abort", onAbort, { once: true });
32
+ });
33
+ }
13
34
  /**
14
35
  * Get the default sandbox API endpoint from environment.
15
36
  *
@@ -115,6 +136,25 @@ class SandboxClient {
115
136
  getApiKey() {
116
137
  return this._apiKey;
117
138
  }
139
+ /**
140
+ * JSON POST helper. Sends JSON body, checks response status,
141
+ * and returns the Response for further processing.
142
+ * Throws on non-ok responses via handleClientHttpError.
143
+ * Callers can add specific status checks (e.g. 404) before calling this.
144
+ * @internal
145
+ */
146
+ async _postJson(url, body, options) {
147
+ const response = await this._fetch(url, {
148
+ method: "POST",
149
+ headers: { "Content-Type": "application/json" },
150
+ body: JSON.stringify(body),
151
+ signal: options?.signal,
152
+ });
153
+ if (!response.ok) {
154
+ await (0, helpers_js_1.handleClientHttpError)(response);
155
+ }
156
+ return response;
157
+ }
118
158
  // =========================================================================
119
159
  // Volume Operations
120
160
  // =========================================================================
@@ -540,14 +580,25 @@ class SandboxClient {
540
580
  * ```
541
581
  */
542
582
  async createSandbox(templateName, options = {}) {
543
- const { name, timeout = 30, waitForReady = true, ttlSeconds, idleTtlSeconds, } = options;
583
+ const { snapshotId, name, timeout = 30, waitForReady = true, ttlSeconds, idleTtlSeconds, vCpus, memBytes, fsCapacityBytes, } = options;
584
+ if (!templateName && !snapshotId) {
585
+ throw new Error("Either templateName or snapshotId is required");
586
+ }
587
+ if (templateName && snapshotId) {
588
+ throw new Error("Cannot specify both templateName and snapshotId");
589
+ }
544
590
  (0, helpers_js_1.validateTtl)(ttlSeconds, "ttlSeconds");
545
591
  (0, helpers_js_1.validateTtl)(idleTtlSeconds, "idleTtlSeconds");
546
592
  const url = `${this._baseUrl}/boxes`;
547
593
  const payload = {
548
- template_name: templateName,
549
594
  wait_for_ready: waitForReady,
550
595
  };
596
+ if (templateName) {
597
+ payload.template_name = templateName;
598
+ }
599
+ if (snapshotId) {
600
+ payload.snapshot_id = snapshotId;
601
+ }
551
602
  if (waitForReady) {
552
603
  payload.timeout = timeout;
553
604
  }
@@ -560,6 +611,15 @@ class SandboxClient {
560
611
  if (idleTtlSeconds !== undefined) {
561
612
  payload.idle_ttl_seconds = idleTtlSeconds;
562
613
  }
614
+ if (vCpus !== undefined) {
615
+ payload.vcpus = vCpus;
616
+ }
617
+ if (memBytes !== undefined) {
618
+ payload.mem_bytes = memBytes;
619
+ }
620
+ if (fsCapacityBytes !== undefined) {
621
+ payload.fs_capacity_bytes = fsCapacityBytes;
622
+ }
563
623
  const httpTimeout = waitForReady ? (timeout + 30) * 1000 : 30 * 1000;
564
624
  const response = await this._fetch(url, {
565
625
  method: "POST",
@@ -582,9 +642,9 @@ class SandboxClient {
582
642
  * @returns Sandbox.
583
643
  * @throws LangSmithResourceNotFoundError if sandbox not found.
584
644
  */
585
- async getSandbox(name) {
645
+ async getSandbox(name, options) {
586
646
  const url = `${this._baseUrl}/boxes/${encodeURIComponent(name)}`;
587
- const response = await this._fetch(url);
647
+ const response = await this._fetch(url, { signal: options?.signal });
588
648
  if (!response.ok) {
589
649
  if (response.status === 404) {
590
650
  throw new errors_js_1.LangSmithResourceNotFoundError(`Sandbox '${name}' not found`, "sandbox");
@@ -679,9 +739,9 @@ class SandboxClient {
679
739
  * @returns ResourceStatus with status and optional status_message.
680
740
  * @throws LangSmithResourceNotFoundError if sandbox not found.
681
741
  */
682
- async getSandboxStatus(name) {
742
+ async getSandboxStatus(name, options) {
683
743
  const url = `${this._baseUrl}/boxes/${encodeURIComponent(name)}/status`;
684
- const response = await this._fetch(url);
744
+ const response = await this._fetch(url, { signal: options?.signal });
685
745
  if (!response.ok) {
686
746
  if (response.status === 404) {
687
747
  throw new errors_js_1.LangSmithResourceNotFoundError(`Sandbox '${name}' not found`, "sandbox");
@@ -711,22 +771,183 @@ class SandboxClient {
711
771
  * ```
712
772
  */
713
773
  async waitForSandbox(name, options = {}) {
714
- const { timeout = 120, pollInterval = 1.0 } = options;
774
+ const { timeout = 120, pollInterval = 1.0, signal } = options;
715
775
  const deadline = Date.now() + timeout * 1000;
716
776
  let lastStatus = "provisioning";
717
777
  while (Date.now() < deadline) {
718
- const statusResult = await this.getSandboxStatus(name);
778
+ signal?.throwIfAborted();
779
+ const statusResult = await this.getSandboxStatus(name, { signal });
719
780
  lastStatus = statusResult.status;
720
781
  if (statusResult.status === "ready") {
721
- return this.getSandbox(name);
782
+ return this.getSandbox(name, { signal });
722
783
  }
723
784
  if (statusResult.status === "failed") {
724
785
  throw new errors_js_1.LangSmithResourceCreationError(statusResult.status_message ?? `Sandbox '${name}' creation failed`, "sandbox");
725
786
  }
726
- // Wait before polling again
727
- await new Promise((resolve) => setTimeout(resolve, pollInterval * 1000));
787
+ // Wait before polling again, capped to remaining time + jitter
788
+ const remaining = deadline - Date.now();
789
+ const jitter = pollInterval * 200 * (Math.random() - 0.5); // ±10%
790
+ const delay = Math.min(pollInterval * 1000 + jitter, remaining);
791
+ if (delay > 0) {
792
+ await sleepWithSignal(delay, signal);
793
+ }
728
794
  }
729
795
  throw new errors_js_1.LangSmithResourceTimeoutError(`Sandbox '${name}' did not become ready within ${timeout}s`, "sandbox", lastStatus);
730
796
  }
797
+ /**
798
+ * Start a stopped sandbox and wait until ready.
799
+ *
800
+ * @param name - Sandbox name.
801
+ * @param options - Options with timeout.
802
+ * @returns Sandbox in "ready" status.
803
+ */
804
+ async startSandbox(name, options = {}) {
805
+ const { timeout = 120, signal } = options;
806
+ const url = `${this._baseUrl}/boxes/${encodeURIComponent(name)}/start`;
807
+ await this._postJson(url, {}, { signal });
808
+ return this.waitForSandbox(name, { timeout, signal });
809
+ }
810
+ /**
811
+ * Stop a running sandbox (preserves sandbox files for later restart).
812
+ *
813
+ * @param name - Sandbox name.
814
+ */
815
+ async stopSandbox(name) {
816
+ const url = `${this._baseUrl}/boxes/${encodeURIComponent(name)}/stop`;
817
+ await this._postJson(url, {});
818
+ }
819
+ // =========================================================================
820
+ // Snapshot Operations
821
+ // =========================================================================
822
+ /**
823
+ * Build a snapshot from a Docker image.
824
+ *
825
+ * Blocks until the snapshot is ready (polls with 2s interval).
826
+ *
827
+ * @param name - Snapshot name.
828
+ * @param dockerImage - Docker image to build from (e.g., "python:3.12-slim").
829
+ * @param fsCapacityBytes - Filesystem capacity in bytes.
830
+ * @param options - Additional options (registry credentials, timeout).
831
+ * @returns Snapshot in "ready" status.
832
+ */
833
+ async createSnapshot(name, dockerImage, fsCapacityBytes, options = {}) {
834
+ const { registryId, registryUrl, registryUsername, registryPassword, timeout = 60, signal, } = options;
835
+ const url = `${this._baseUrl}/snapshots`;
836
+ const payload = {
837
+ name,
838
+ docker_image: dockerImage,
839
+ fs_capacity_bytes: fsCapacityBytes,
840
+ };
841
+ if (registryId !== undefined) {
842
+ payload.registry_id = registryId;
843
+ }
844
+ if (registryUrl !== undefined) {
845
+ payload.registry_url = registryUrl;
846
+ }
847
+ if (registryUsername !== undefined) {
848
+ payload.registry_username = registryUsername;
849
+ }
850
+ if (registryPassword !== undefined) {
851
+ payload.registry_password = registryPassword;
852
+ }
853
+ const response = await this._postJson(url, payload, { signal });
854
+ const snapshot = (await response.json());
855
+ return this.waitForSnapshot(snapshot.id, { timeout, signal });
856
+ }
857
+ /**
858
+ * Capture a snapshot from a running sandbox.
859
+ *
860
+ * Blocks until the snapshot is ready (polls with 2s interval).
861
+ *
862
+ * @param sandboxName - Name of the sandbox to capture from.
863
+ * @param name - Snapshot name.
864
+ * @param options - Capture options (checkpoint, timeout).
865
+ * @returns Snapshot in "ready" status.
866
+ */
867
+ async captureSnapshot(sandboxName, name, options = {}) {
868
+ const { checkpoint, timeout = 60, signal } = options;
869
+ const url = `${this._baseUrl}/boxes/${encodeURIComponent(sandboxName)}/snapshot`;
870
+ const payload = { name };
871
+ if (checkpoint !== undefined) {
872
+ payload.checkpoint = checkpoint;
873
+ }
874
+ const response = await this._postJson(url, payload, { signal });
875
+ const snapshot = (await response.json());
876
+ return this.waitForSnapshot(snapshot.id, { timeout, signal });
877
+ }
878
+ /**
879
+ * Get a snapshot by ID.
880
+ *
881
+ * @param snapshotId - Snapshot UUID.
882
+ * @returns Snapshot.
883
+ */
884
+ async getSnapshot(snapshotId, options) {
885
+ const url = `${this._baseUrl}/snapshots/${encodeURIComponent(snapshotId)}`;
886
+ const response = await this._fetch(url, { signal: options?.signal });
887
+ if (!response.ok) {
888
+ if (response.status === 404) {
889
+ throw new errors_js_1.LangSmithResourceNotFoundError(`Snapshot '${snapshotId}' not found`, "snapshot");
890
+ }
891
+ await (0, helpers_js_1.handleClientHttpError)(response);
892
+ }
893
+ return (await response.json());
894
+ }
895
+ /**
896
+ * List all snapshots.
897
+ *
898
+ * @returns List of Snapshots.
899
+ */
900
+ async listSnapshots() {
901
+ const url = `${this._baseUrl}/snapshots`;
902
+ const response = await this._fetch(url);
903
+ if (!response.ok) {
904
+ await (0, helpers_js_1.handleClientHttpError)(response);
905
+ }
906
+ const data = await response.json();
907
+ return (data.snapshots ?? []);
908
+ }
909
+ /**
910
+ * Delete a snapshot.
911
+ *
912
+ * @param snapshotId - Snapshot UUID.
913
+ */
914
+ async deleteSnapshot(snapshotId) {
915
+ const url = `${this._baseUrl}/snapshots/${encodeURIComponent(snapshotId)}`;
916
+ const response = await this._fetch(url, { method: "DELETE" });
917
+ if (!response.ok) {
918
+ await (0, helpers_js_1.handleClientHttpError)(response);
919
+ }
920
+ }
921
+ /**
922
+ * Poll until a snapshot reaches "ready" or "failed" status.
923
+ *
924
+ * @param snapshotId - Snapshot UUID.
925
+ * @param options - Polling options (timeout, pollInterval).
926
+ * @returns Snapshot in "ready" status.
927
+ */
928
+ async waitForSnapshot(snapshotId, options = {}) {
929
+ const { timeout = 300, pollInterval = 2.0, signal } = options;
930
+ const deadline = Date.now() + timeout * 1000;
931
+ let lastStatus = "building";
932
+ while (Date.now() < deadline) {
933
+ signal?.throwIfAborted();
934
+ const snapshot = await this.getSnapshot(snapshotId, { signal });
935
+ lastStatus = snapshot.status;
936
+ if (snapshot.status === "ready") {
937
+ return snapshot;
938
+ }
939
+ if (snapshot.status === "failed") {
940
+ throw new errors_js_1.LangSmithResourceCreationError(snapshot.status_message ?? `Snapshot '${snapshotId}' build failed`, "snapshot");
941
+ }
942
+ // Cap sleep to remaining time + jitter
943
+ const remaining = deadline - Date.now();
944
+ const jitter = pollInterval * 200 * (Math.random() - 0.5); // ±10%
945
+ const delay = Math.min(pollInterval * 1000 + jitter, remaining);
946
+ if (delay > 0) {
947
+ await sleepWithSignal(delay, signal);
948
+ }
949
+ }
950
+ throw new errors_js_1.LangSmithResourceTimeoutError(`Snapshot '${snapshotId}' did not become ready within ${timeout}s`, "snapshot", lastStatus);
951
+ }
731
952
  }
732
953
  exports.SandboxClient = SandboxClient;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Main SandboxClient class for interacting with the sandbox server API.
3
3
  */
4
- import type { CreatePoolOptions, CreateSandboxOptions, CreateTemplateOptions, CreateVolumeOptions, Pool, ResourceStatus, SandboxClientConfig, SandboxTemplate, UpdatePoolOptions, UpdateSandboxOptions, UpdateTemplateOptions, UpdateVolumeOptions, Volume, WaitForSandboxOptions } from "./types.js";
4
+ import type { CaptureSnapshotOptions, CreatePoolOptions, CreateSandboxOptions, CreateSnapshotOptions, CreateTemplateOptions, CreateVolumeOptions, Pool, ResourceStatus, SandboxClientConfig, SandboxTemplate, Snapshot, StartSandboxOptions, UpdatePoolOptions, UpdateSandboxOptions, UpdateTemplateOptions, UpdateVolumeOptions, Volume, WaitForSandboxOptions, WaitForSnapshotOptions } from "./types.js";
5
5
  import { Sandbox } from "./sandbox.js";
6
6
  /**
7
7
  * Client for interacting with the Sandbox Server API.
@@ -206,7 +206,7 @@ export declare class SandboxClient {
206
206
  * }
207
207
  * ```
208
208
  */
209
- createSandbox(templateName: string, options?: CreateSandboxOptions): Promise<Sandbox>;
209
+ createSandbox(templateName?: string, options?: CreateSandboxOptions): Promise<Sandbox>;
210
210
  /**
211
211
  * Get a Sandbox by name.
212
212
  *
@@ -216,7 +216,9 @@ export declare class SandboxClient {
216
216
  * @returns Sandbox.
217
217
  * @throws LangSmithResourceNotFoundError if sandbox not found.
218
218
  */
219
- getSandbox(name: string): Promise<Sandbox>;
219
+ getSandbox(name: string, options?: {
220
+ signal?: AbortSignal;
221
+ }): Promise<Sandbox>;
220
222
  /**
221
223
  * List all Sandboxes.
222
224
  *
@@ -258,7 +260,9 @@ export declare class SandboxClient {
258
260
  * @returns ResourceStatus with status and optional status_message.
259
261
  * @throws LangSmithResourceNotFoundError if sandbox not found.
260
262
  */
261
- getSandboxStatus(name: string): Promise<ResourceStatus>;
263
+ getSandboxStatus(name: string, options?: {
264
+ signal?: AbortSignal;
265
+ }): Promise<ResourceStatus>;
262
266
  /**
263
267
  * Wait for a sandbox to become ready.
264
268
  *
@@ -280,4 +284,70 @@ export declare class SandboxClient {
280
284
  * ```
281
285
  */
282
286
  waitForSandbox(name: string, options?: WaitForSandboxOptions): Promise<Sandbox>;
287
+ /**
288
+ * Start a stopped sandbox and wait until ready.
289
+ *
290
+ * @param name - Sandbox name.
291
+ * @param options - Options with timeout.
292
+ * @returns Sandbox in "ready" status.
293
+ */
294
+ startSandbox(name: string, options?: StartSandboxOptions): Promise<Sandbox>;
295
+ /**
296
+ * Stop a running sandbox (preserves sandbox files for later restart).
297
+ *
298
+ * @param name - Sandbox name.
299
+ */
300
+ stopSandbox(name: string): Promise<void>;
301
+ /**
302
+ * Build a snapshot from a Docker image.
303
+ *
304
+ * Blocks until the snapshot is ready (polls with 2s interval).
305
+ *
306
+ * @param name - Snapshot name.
307
+ * @param dockerImage - Docker image to build from (e.g., "python:3.12-slim").
308
+ * @param fsCapacityBytes - Filesystem capacity in bytes.
309
+ * @param options - Additional options (registry credentials, timeout).
310
+ * @returns Snapshot in "ready" status.
311
+ */
312
+ createSnapshot(name: string, dockerImage: string, fsCapacityBytes: number, options?: CreateSnapshotOptions): Promise<Snapshot>;
313
+ /**
314
+ * Capture a snapshot from a running sandbox.
315
+ *
316
+ * Blocks until the snapshot is ready (polls with 2s interval).
317
+ *
318
+ * @param sandboxName - Name of the sandbox to capture from.
319
+ * @param name - Snapshot name.
320
+ * @param options - Capture options (checkpoint, timeout).
321
+ * @returns Snapshot in "ready" status.
322
+ */
323
+ captureSnapshot(sandboxName: string, name: string, options?: CaptureSnapshotOptions): Promise<Snapshot>;
324
+ /**
325
+ * Get a snapshot by ID.
326
+ *
327
+ * @param snapshotId - Snapshot UUID.
328
+ * @returns Snapshot.
329
+ */
330
+ getSnapshot(snapshotId: string, options?: {
331
+ signal?: AbortSignal;
332
+ }): Promise<Snapshot>;
333
+ /**
334
+ * List all snapshots.
335
+ *
336
+ * @returns List of Snapshots.
337
+ */
338
+ listSnapshots(): Promise<Snapshot[]>;
339
+ /**
340
+ * Delete a snapshot.
341
+ *
342
+ * @param snapshotId - Snapshot UUID.
343
+ */
344
+ deleteSnapshot(snapshotId: string): Promise<void>;
345
+ /**
346
+ * Poll until a snapshot reaches "ready" or "failed" status.
347
+ *
348
+ * @param snapshotId - Snapshot UUID.
349
+ * @param options - Polling options (timeout, pollInterval).
350
+ * @returns Snapshot in "ready" status.
351
+ */
352
+ waitForSnapshot(snapshotId: string, options?: WaitForSnapshotOptions): Promise<Snapshot>;
283
353
  }
@@ -7,6 +7,27 @@ import { AsyncCaller } from "../../utils/async_caller.js";
7
7
  import { Sandbox } from "./sandbox.js";
8
8
  import { LangSmithResourceCreationError, LangSmithResourceNameConflictError, LangSmithResourceNotFoundError, LangSmithResourceTimeoutError, LangSmithSandboxAPIError, } from "./errors.js";
9
9
  import { handleClientHttpError, handleConflictError, handlePoolError, handleResourceInUseError, handleSandboxCreationError, handleVolumeCreationError, validateTtl, } from "./helpers.js";
10
+ /**
11
+ * Sleep that can be interrupted by an AbortSignal.
12
+ * Resolves after `ms` milliseconds or rejects immediately if the signal fires.
13
+ */
14
+ function sleepWithSignal(ms, signal) {
15
+ if (!signal) {
16
+ return new Promise((resolve) => setTimeout(resolve, ms));
17
+ }
18
+ signal.throwIfAborted();
19
+ return new Promise((resolve, reject) => {
20
+ const timer = setTimeout(() => {
21
+ signal.removeEventListener("abort", onAbort);
22
+ resolve();
23
+ }, ms);
24
+ function onAbort() {
25
+ clearTimeout(timer);
26
+ reject(signal.reason);
27
+ }
28
+ signal.addEventListener("abort", onAbort, { once: true });
29
+ });
30
+ }
10
31
  /**
11
32
  * Get the default sandbox API endpoint from environment.
12
33
  *
@@ -112,6 +133,25 @@ export class SandboxClient {
112
133
  getApiKey() {
113
134
  return this._apiKey;
114
135
  }
136
+ /**
137
+ * JSON POST helper. Sends JSON body, checks response status,
138
+ * and returns the Response for further processing.
139
+ * Throws on non-ok responses via handleClientHttpError.
140
+ * Callers can add specific status checks (e.g. 404) before calling this.
141
+ * @internal
142
+ */
143
+ async _postJson(url, body, options) {
144
+ const response = await this._fetch(url, {
145
+ method: "POST",
146
+ headers: { "Content-Type": "application/json" },
147
+ body: JSON.stringify(body),
148
+ signal: options?.signal,
149
+ });
150
+ if (!response.ok) {
151
+ await handleClientHttpError(response);
152
+ }
153
+ return response;
154
+ }
115
155
  // =========================================================================
116
156
  // Volume Operations
117
157
  // =========================================================================
@@ -537,14 +577,25 @@ export class SandboxClient {
537
577
  * ```
538
578
  */
539
579
  async createSandbox(templateName, options = {}) {
540
- const { name, timeout = 30, waitForReady = true, ttlSeconds, idleTtlSeconds, } = options;
580
+ const { snapshotId, name, timeout = 30, waitForReady = true, ttlSeconds, idleTtlSeconds, vCpus, memBytes, fsCapacityBytes, } = options;
581
+ if (!templateName && !snapshotId) {
582
+ throw new Error("Either templateName or snapshotId is required");
583
+ }
584
+ if (templateName && snapshotId) {
585
+ throw new Error("Cannot specify both templateName and snapshotId");
586
+ }
541
587
  validateTtl(ttlSeconds, "ttlSeconds");
542
588
  validateTtl(idleTtlSeconds, "idleTtlSeconds");
543
589
  const url = `${this._baseUrl}/boxes`;
544
590
  const payload = {
545
- template_name: templateName,
546
591
  wait_for_ready: waitForReady,
547
592
  };
593
+ if (templateName) {
594
+ payload.template_name = templateName;
595
+ }
596
+ if (snapshotId) {
597
+ payload.snapshot_id = snapshotId;
598
+ }
548
599
  if (waitForReady) {
549
600
  payload.timeout = timeout;
550
601
  }
@@ -557,6 +608,15 @@ export class SandboxClient {
557
608
  if (idleTtlSeconds !== undefined) {
558
609
  payload.idle_ttl_seconds = idleTtlSeconds;
559
610
  }
611
+ if (vCpus !== undefined) {
612
+ payload.vcpus = vCpus;
613
+ }
614
+ if (memBytes !== undefined) {
615
+ payload.mem_bytes = memBytes;
616
+ }
617
+ if (fsCapacityBytes !== undefined) {
618
+ payload.fs_capacity_bytes = fsCapacityBytes;
619
+ }
560
620
  const httpTimeout = waitForReady ? (timeout + 30) * 1000 : 30 * 1000;
561
621
  const response = await this._fetch(url, {
562
622
  method: "POST",
@@ -579,9 +639,9 @@ export class SandboxClient {
579
639
  * @returns Sandbox.
580
640
  * @throws LangSmithResourceNotFoundError if sandbox not found.
581
641
  */
582
- async getSandbox(name) {
642
+ async getSandbox(name, options) {
583
643
  const url = `${this._baseUrl}/boxes/${encodeURIComponent(name)}`;
584
- const response = await this._fetch(url);
644
+ const response = await this._fetch(url, { signal: options?.signal });
585
645
  if (!response.ok) {
586
646
  if (response.status === 404) {
587
647
  throw new LangSmithResourceNotFoundError(`Sandbox '${name}' not found`, "sandbox");
@@ -676,9 +736,9 @@ export class SandboxClient {
676
736
  * @returns ResourceStatus with status and optional status_message.
677
737
  * @throws LangSmithResourceNotFoundError if sandbox not found.
678
738
  */
679
- async getSandboxStatus(name) {
739
+ async getSandboxStatus(name, options) {
680
740
  const url = `${this._baseUrl}/boxes/${encodeURIComponent(name)}/status`;
681
- const response = await this._fetch(url);
741
+ const response = await this._fetch(url, { signal: options?.signal });
682
742
  if (!response.ok) {
683
743
  if (response.status === 404) {
684
744
  throw new LangSmithResourceNotFoundError(`Sandbox '${name}' not found`, "sandbox");
@@ -708,21 +768,182 @@ export class SandboxClient {
708
768
  * ```
709
769
  */
710
770
  async waitForSandbox(name, options = {}) {
711
- const { timeout = 120, pollInterval = 1.0 } = options;
771
+ const { timeout = 120, pollInterval = 1.0, signal } = options;
712
772
  const deadline = Date.now() + timeout * 1000;
713
773
  let lastStatus = "provisioning";
714
774
  while (Date.now() < deadline) {
715
- const statusResult = await this.getSandboxStatus(name);
775
+ signal?.throwIfAborted();
776
+ const statusResult = await this.getSandboxStatus(name, { signal });
716
777
  lastStatus = statusResult.status;
717
778
  if (statusResult.status === "ready") {
718
- return this.getSandbox(name);
779
+ return this.getSandbox(name, { signal });
719
780
  }
720
781
  if (statusResult.status === "failed") {
721
782
  throw new LangSmithResourceCreationError(statusResult.status_message ?? `Sandbox '${name}' creation failed`, "sandbox");
722
783
  }
723
- // Wait before polling again
724
- await new Promise((resolve) => setTimeout(resolve, pollInterval * 1000));
784
+ // Wait before polling again, capped to remaining time + jitter
785
+ const remaining = deadline - Date.now();
786
+ const jitter = pollInterval * 200 * (Math.random() - 0.5); // ±10%
787
+ const delay = Math.min(pollInterval * 1000 + jitter, remaining);
788
+ if (delay > 0) {
789
+ await sleepWithSignal(delay, signal);
790
+ }
725
791
  }
726
792
  throw new LangSmithResourceTimeoutError(`Sandbox '${name}' did not become ready within ${timeout}s`, "sandbox", lastStatus);
727
793
  }
794
+ /**
795
+ * Start a stopped sandbox and wait until ready.
796
+ *
797
+ * @param name - Sandbox name.
798
+ * @param options - Options with timeout.
799
+ * @returns Sandbox in "ready" status.
800
+ */
801
+ async startSandbox(name, options = {}) {
802
+ const { timeout = 120, signal } = options;
803
+ const url = `${this._baseUrl}/boxes/${encodeURIComponent(name)}/start`;
804
+ await this._postJson(url, {}, { signal });
805
+ return this.waitForSandbox(name, { timeout, signal });
806
+ }
807
+ /**
808
+ * Stop a running sandbox (preserves sandbox files for later restart).
809
+ *
810
+ * @param name - Sandbox name.
811
+ */
812
+ async stopSandbox(name) {
813
+ const url = `${this._baseUrl}/boxes/${encodeURIComponent(name)}/stop`;
814
+ await this._postJson(url, {});
815
+ }
816
+ // =========================================================================
817
+ // Snapshot Operations
818
+ // =========================================================================
819
+ /**
820
+ * Build a snapshot from a Docker image.
821
+ *
822
+ * Blocks until the snapshot is ready (polls with 2s interval).
823
+ *
824
+ * @param name - Snapshot name.
825
+ * @param dockerImage - Docker image to build from (e.g., "python:3.12-slim").
826
+ * @param fsCapacityBytes - Filesystem capacity in bytes.
827
+ * @param options - Additional options (registry credentials, timeout).
828
+ * @returns Snapshot in "ready" status.
829
+ */
830
+ async createSnapshot(name, dockerImage, fsCapacityBytes, options = {}) {
831
+ const { registryId, registryUrl, registryUsername, registryPassword, timeout = 60, signal, } = options;
832
+ const url = `${this._baseUrl}/snapshots`;
833
+ const payload = {
834
+ name,
835
+ docker_image: dockerImage,
836
+ fs_capacity_bytes: fsCapacityBytes,
837
+ };
838
+ if (registryId !== undefined) {
839
+ payload.registry_id = registryId;
840
+ }
841
+ if (registryUrl !== undefined) {
842
+ payload.registry_url = registryUrl;
843
+ }
844
+ if (registryUsername !== undefined) {
845
+ payload.registry_username = registryUsername;
846
+ }
847
+ if (registryPassword !== undefined) {
848
+ payload.registry_password = registryPassword;
849
+ }
850
+ const response = await this._postJson(url, payload, { signal });
851
+ const snapshot = (await response.json());
852
+ return this.waitForSnapshot(snapshot.id, { timeout, signal });
853
+ }
854
+ /**
855
+ * Capture a snapshot from a running sandbox.
856
+ *
857
+ * Blocks until the snapshot is ready (polls with 2s interval).
858
+ *
859
+ * @param sandboxName - Name of the sandbox to capture from.
860
+ * @param name - Snapshot name.
861
+ * @param options - Capture options (checkpoint, timeout).
862
+ * @returns Snapshot in "ready" status.
863
+ */
864
+ async captureSnapshot(sandboxName, name, options = {}) {
865
+ const { checkpoint, timeout = 60, signal } = options;
866
+ const url = `${this._baseUrl}/boxes/${encodeURIComponent(sandboxName)}/snapshot`;
867
+ const payload = { name };
868
+ if (checkpoint !== undefined) {
869
+ payload.checkpoint = checkpoint;
870
+ }
871
+ const response = await this._postJson(url, payload, { signal });
872
+ const snapshot = (await response.json());
873
+ return this.waitForSnapshot(snapshot.id, { timeout, signal });
874
+ }
875
+ /**
876
+ * Get a snapshot by ID.
877
+ *
878
+ * @param snapshotId - Snapshot UUID.
879
+ * @returns Snapshot.
880
+ */
881
+ async getSnapshot(snapshotId, options) {
882
+ const url = `${this._baseUrl}/snapshots/${encodeURIComponent(snapshotId)}`;
883
+ const response = await this._fetch(url, { signal: options?.signal });
884
+ if (!response.ok) {
885
+ if (response.status === 404) {
886
+ throw new LangSmithResourceNotFoundError(`Snapshot '${snapshotId}' not found`, "snapshot");
887
+ }
888
+ await handleClientHttpError(response);
889
+ }
890
+ return (await response.json());
891
+ }
892
+ /**
893
+ * List all snapshots.
894
+ *
895
+ * @returns List of Snapshots.
896
+ */
897
+ async listSnapshots() {
898
+ const url = `${this._baseUrl}/snapshots`;
899
+ const response = await this._fetch(url);
900
+ if (!response.ok) {
901
+ await handleClientHttpError(response);
902
+ }
903
+ const data = await response.json();
904
+ return (data.snapshots ?? []);
905
+ }
906
+ /**
907
+ * Delete a snapshot.
908
+ *
909
+ * @param snapshotId - Snapshot UUID.
910
+ */
911
+ async deleteSnapshot(snapshotId) {
912
+ const url = `${this._baseUrl}/snapshots/${encodeURIComponent(snapshotId)}`;
913
+ const response = await this._fetch(url, { method: "DELETE" });
914
+ if (!response.ok) {
915
+ await handleClientHttpError(response);
916
+ }
917
+ }
918
+ /**
919
+ * Poll until a snapshot reaches "ready" or "failed" status.
920
+ *
921
+ * @param snapshotId - Snapshot UUID.
922
+ * @param options - Polling options (timeout, pollInterval).
923
+ * @returns Snapshot in "ready" status.
924
+ */
925
+ async waitForSnapshot(snapshotId, options = {}) {
926
+ const { timeout = 300, pollInterval = 2.0, signal } = options;
927
+ const deadline = Date.now() + timeout * 1000;
928
+ let lastStatus = "building";
929
+ while (Date.now() < deadline) {
930
+ signal?.throwIfAborted();
931
+ const snapshot = await this.getSnapshot(snapshotId, { signal });
932
+ lastStatus = snapshot.status;
933
+ if (snapshot.status === "ready") {
934
+ return snapshot;
935
+ }
936
+ if (snapshot.status === "failed") {
937
+ throw new LangSmithResourceCreationError(snapshot.status_message ?? `Snapshot '${snapshotId}' build failed`, "snapshot");
938
+ }
939
+ // Cap sleep to remaining time + jitter
940
+ const remaining = deadline - Date.now();
941
+ const jitter = pollInterval * 200 * (Math.random() - 0.5); // ±10%
942
+ const delay = Math.min(pollInterval * 1000 + jitter, remaining);
943
+ if (delay > 0) {
944
+ await sleepWithSignal(delay, signal);
945
+ }
946
+ }
947
+ throw new LangSmithResourceTimeoutError(`Snapshot '${snapshotId}' did not become ready within ${timeout}s`, "snapshot", lastStatus);
948
+ }
728
949
  }
@@ -25,5 +25,5 @@
25
25
  export { SandboxClient } from "./client.js";
26
26
  export { Sandbox } from "./sandbox.js";
27
27
  export { CommandHandle } from "./command_handle.js";
28
- export type { ExecutionResult, OutputChunk, WsMessage, WsRunOptions, ResourceSpec, ResourceStatus, VolumeMountSpec, Volume, SandboxTemplate, Pool, SandboxData, SandboxClientConfig, RunOptions, CreateSandboxOptions, UpdateSandboxOptions, WaitForSandboxOptions, CreateVolumeOptions, CreateTemplateOptions, UpdateTemplateOptions, CreatePoolOptions, UpdateVolumeOptions, UpdatePoolOptions, } from "./types.js";
28
+ export type { ExecutionResult, OutputChunk, WsMessage, WsRunOptions, ResourceSpec, ResourceStatus, Snapshot, VolumeMountSpec, Volume, SandboxTemplate, Pool, SandboxData, SandboxClientConfig, RunOptions, CreateSandboxOptions, CreateSnapshotOptions, CaptureSnapshotOptions, WaitForSnapshotOptions, StartSandboxOptions, UpdateSandboxOptions, WaitForSandboxOptions, CreateVolumeOptions, CreateTemplateOptions, UpdateTemplateOptions, CreatePoolOptions, UpdateVolumeOptions, UpdatePoolOptions, } from "./types.js";
29
29
  export { LangSmithSandboxError, LangSmithSandboxAPIError, LangSmithSandboxAuthenticationError, LangSmithSandboxConnectionError, LangSmithSandboxServerReloadError, LangSmithResourceNotFoundError, LangSmithResourceTimeoutError, LangSmithResourceInUseError, LangSmithResourceAlreadyExistsError, LangSmithResourceNameConflictError, LangSmithValidationError, LangSmithQuotaExceededError, LangSmithResourceCreationError, LangSmithSandboxCreationError, LangSmithSandboxNotReadyError, LangSmithSandboxOperationError, LangSmithCommandTimeoutError, LangSmithDataplaneNotConfiguredError, } from "./errors.js";
@@ -52,7 +52,7 @@ class Sandbox {
52
52
  writable: true,
53
53
  value: void 0
54
54
  });
55
- /** Provisioning status ("provisioning", "ready", "failed"). */
55
+ /** Provisioning status ("provisioning", "ready", "failed", "stopped"). */
56
56
  Object.defineProperty(this, "status", {
57
57
  enumerable: true,
58
58
  configurable: true,
@@ -108,6 +108,34 @@ class Sandbox {
108
108
  writable: true,
109
109
  value: void 0
110
110
  });
111
+ /** Snapshot ID used to create this sandbox. */
112
+ Object.defineProperty(this, "snapshot_id", {
113
+ enumerable: true,
114
+ configurable: true,
115
+ writable: true,
116
+ value: void 0
117
+ });
118
+ /** Number of vCPUs allocated. */
119
+ Object.defineProperty(this, "vCpus", {
120
+ enumerable: true,
121
+ configurable: true,
122
+ writable: true,
123
+ value: void 0
124
+ });
125
+ /** Memory allocation in bytes. */
126
+ Object.defineProperty(this, "mem_bytes", {
127
+ enumerable: true,
128
+ configurable: true,
129
+ writable: true,
130
+ value: void 0
131
+ });
132
+ /** Root filesystem capacity in bytes. */
133
+ Object.defineProperty(this, "fs_capacity_bytes", {
134
+ enumerable: true,
135
+ configurable: true,
136
+ writable: true,
137
+ value: void 0
138
+ });
111
139
  Object.defineProperty(this, "_client", {
112
140
  enumerable: true,
113
141
  configurable: true,
@@ -125,6 +153,10 @@ class Sandbox {
125
153
  this.ttl_seconds = data.ttl_seconds;
126
154
  this.idle_ttl_seconds = data.idle_ttl_seconds;
127
155
  this.expires_at = data.expires_at;
156
+ this.snapshot_id = data.snapshot_id;
157
+ this.vCpus = data.vcpus;
158
+ this.mem_bytes = data.mem_bytes;
159
+ this.fs_capacity_bytes = data.fs_capacity_bytes;
128
160
  this._client = client;
129
161
  }
130
162
  /**
@@ -339,5 +371,35 @@ class Sandbox {
339
371
  async delete() {
340
372
  await this._client.deleteSandbox(this.name);
341
373
  }
374
+ /**
375
+ * Start a stopped sandbox and wait until ready.
376
+ *
377
+ * Updates this sandbox's status and dataplane_url in place.
378
+ *
379
+ * @param timeout - Timeout in seconds when waiting for ready. Default: 120.
380
+ */
381
+ async start(options = {}) {
382
+ const refreshed = await this._client.startSandbox(this.name, options);
383
+ this.status = refreshed.status;
384
+ this.dataplane_url = refreshed.dataplane_url;
385
+ }
386
+ /**
387
+ * Stop a running sandbox (preserves sandbox files for later restart).
388
+ */
389
+ async stop() {
390
+ await this._client.stopSandbox(this.name);
391
+ this.status = "stopped";
392
+ this.dataplane_url = undefined;
393
+ }
394
+ /**
395
+ * Capture a snapshot from this sandbox.
396
+ *
397
+ * @param name - Snapshot name.
398
+ * @param options - Capture options (checkpoint, timeout).
399
+ * @returns Snapshot in "ready" status.
400
+ */
401
+ async captureSnapshot(name, options = {}) {
402
+ return this._client.captureSnapshot(this.name, name, options);
403
+ }
342
404
  }
343
405
  exports.Sandbox = Sandbox;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Sandbox class for interacting with a specific sandbox instance.
3
3
  */
4
- import type { ExecutionResult, RunOptions } from "./types.js";
4
+ import type { CaptureSnapshotOptions, ExecutionResult, RunOptions, Snapshot, StartSandboxOptions } from "./types.js";
5
5
  import { CommandHandle } from "./command_handle.js";
6
6
  /**
7
7
  * Represents an active sandbox for running commands and file operations.
@@ -27,11 +27,11 @@ export declare class Sandbox {
27
27
  /** Display name (can be updated). */
28
28
  readonly name: string;
29
29
  /** Name of the template used to create this sandbox. */
30
- readonly template_name: string;
30
+ readonly template_name?: string;
31
31
  /** URL for data plane operations (file I/O, command execution). */
32
- readonly dataplane_url?: string;
33
- /** Provisioning status ("provisioning", "ready", "failed"). */
34
- readonly status?: string;
32
+ dataplane_url?: string;
33
+ /** Provisioning status ("provisioning", "ready", "failed", "stopped"). */
34
+ status?: string;
35
35
  /** Human-readable status message (e.g., error details when failed). */
36
36
  readonly status_message?: string;
37
37
  /** Unique identifier (UUID). Remains constant even if name changes. */
@@ -46,6 +46,14 @@ export declare class Sandbox {
46
46
  readonly idle_ttl_seconds?: number;
47
47
  /** Computed expiration timestamp when a TTL is active. */
48
48
  readonly expires_at?: string;
49
+ /** Snapshot ID used to create this sandbox. */
50
+ readonly snapshot_id?: string;
51
+ /** Number of vCPUs allocated. */
52
+ readonly vCpus?: number;
53
+ /** Memory allocation in bytes. */
54
+ readonly mem_bytes?: number;
55
+ /** Root filesystem capacity in bytes. */
56
+ readonly fs_capacity_bytes?: number;
49
57
  private _client;
50
58
  /**
51
59
  * Validate and return the dataplane URL.
@@ -147,4 +155,24 @@ export declare class Sandbox {
147
155
  * ```
148
156
  */
149
157
  delete(): Promise<void>;
158
+ /**
159
+ * Start a stopped sandbox and wait until ready.
160
+ *
161
+ * Updates this sandbox's status and dataplane_url in place.
162
+ *
163
+ * @param timeout - Timeout in seconds when waiting for ready. Default: 120.
164
+ */
165
+ start(options?: StartSandboxOptions): Promise<void>;
166
+ /**
167
+ * Stop a running sandbox (preserves sandbox files for later restart).
168
+ */
169
+ stop(): Promise<void>;
170
+ /**
171
+ * Capture a snapshot from this sandbox.
172
+ *
173
+ * @param name - Snapshot name.
174
+ * @param options - Capture options (checkpoint, timeout).
175
+ * @returns Snapshot in "ready" status.
176
+ */
177
+ captureSnapshot(name: string, options?: CaptureSnapshotOptions): Promise<Snapshot>;
150
178
  }
@@ -49,7 +49,7 @@ export class Sandbox {
49
49
  writable: true,
50
50
  value: void 0
51
51
  });
52
- /** Provisioning status ("provisioning", "ready", "failed"). */
52
+ /** Provisioning status ("provisioning", "ready", "failed", "stopped"). */
53
53
  Object.defineProperty(this, "status", {
54
54
  enumerable: true,
55
55
  configurable: true,
@@ -105,6 +105,34 @@ export class Sandbox {
105
105
  writable: true,
106
106
  value: void 0
107
107
  });
108
+ /** Snapshot ID used to create this sandbox. */
109
+ Object.defineProperty(this, "snapshot_id", {
110
+ enumerable: true,
111
+ configurable: true,
112
+ writable: true,
113
+ value: void 0
114
+ });
115
+ /** Number of vCPUs allocated. */
116
+ Object.defineProperty(this, "vCpus", {
117
+ enumerable: true,
118
+ configurable: true,
119
+ writable: true,
120
+ value: void 0
121
+ });
122
+ /** Memory allocation in bytes. */
123
+ Object.defineProperty(this, "mem_bytes", {
124
+ enumerable: true,
125
+ configurable: true,
126
+ writable: true,
127
+ value: void 0
128
+ });
129
+ /** Root filesystem capacity in bytes. */
130
+ Object.defineProperty(this, "fs_capacity_bytes", {
131
+ enumerable: true,
132
+ configurable: true,
133
+ writable: true,
134
+ value: void 0
135
+ });
108
136
  Object.defineProperty(this, "_client", {
109
137
  enumerable: true,
110
138
  configurable: true,
@@ -122,6 +150,10 @@ export class Sandbox {
122
150
  this.ttl_seconds = data.ttl_seconds;
123
151
  this.idle_ttl_seconds = data.idle_ttl_seconds;
124
152
  this.expires_at = data.expires_at;
153
+ this.snapshot_id = data.snapshot_id;
154
+ this.vCpus = data.vcpus;
155
+ this.mem_bytes = data.mem_bytes;
156
+ this.fs_capacity_bytes = data.fs_capacity_bytes;
125
157
  this._client = client;
126
158
  }
127
159
  /**
@@ -336,4 +368,34 @@ export class Sandbox {
336
368
  async delete() {
337
369
  await this._client.deleteSandbox(this.name);
338
370
  }
371
+ /**
372
+ * Start a stopped sandbox and wait until ready.
373
+ *
374
+ * Updates this sandbox's status and dataplane_url in place.
375
+ *
376
+ * @param timeout - Timeout in seconds when waiting for ready. Default: 120.
377
+ */
378
+ async start(options = {}) {
379
+ const refreshed = await this._client.startSandbox(this.name, options);
380
+ this.status = refreshed.status;
381
+ this.dataplane_url = refreshed.dataplane_url;
382
+ }
383
+ /**
384
+ * Stop a running sandbox (preserves sandbox files for later restart).
385
+ */
386
+ async stop() {
387
+ await this._client.stopSandbox(this.name);
388
+ this.status = "stopped";
389
+ this.dataplane_url = undefined;
390
+ }
391
+ /**
392
+ * Capture a snapshot from this sandbox.
393
+ *
394
+ * @param name - Snapshot name.
395
+ * @param options - Capture options (checkpoint, timeout).
396
+ * @returns Snapshot in "ready" status.
397
+ */
398
+ async captureSnapshot(name, options = {}) {
399
+ return this._client.captureSnapshot(this.name, name, options);
400
+ }
339
401
  }
@@ -73,13 +73,35 @@ export interface ResourceStatus {
73
73
  /** Human-readable details when failed. */
74
74
  status_message?: string;
75
75
  }
76
+ /**
77
+ * Represents a sandbox snapshot.
78
+ *
79
+ * Snapshots are built from Docker images or captured from running sandboxes.
80
+ * They are used to create new sandboxes.
81
+ */
82
+ export interface Snapshot {
83
+ id: string;
84
+ name: string;
85
+ /** One of "building", "ready", "failed". */
86
+ status: string;
87
+ fs_capacity_bytes: number;
88
+ docker_image?: string;
89
+ image_digest?: string;
90
+ source_sandbox_id?: string;
91
+ status_message?: string;
92
+ fs_used_bytes?: number;
93
+ created_by?: string;
94
+ registry_id?: string;
95
+ created_at?: string;
96
+ updated_at?: string;
97
+ }
76
98
  /**
77
99
  * Data representing a sandbox instance from the API.
78
100
  */
79
101
  export interface SandboxData {
80
102
  id?: string;
81
103
  name: string;
82
- template_name: string;
104
+ template_name?: string;
83
105
  dataplane_url?: string;
84
106
  status?: string;
85
107
  status_message?: string;
@@ -91,6 +113,14 @@ export interface SandboxData {
91
113
  idle_ttl_seconds?: number;
92
114
  /** Computed expiration timestamp when a TTL is active, else omitted/`undefined`. */
93
115
  expires_at?: string;
116
+ /** Snapshot ID used to create this sandbox. */
117
+ snapshot_id?: string;
118
+ /** Number of vCPUs allocated. */
119
+ vcpus?: number;
120
+ /** Memory allocation in bytes. */
121
+ mem_bytes?: number;
122
+ /** Root filesystem capacity in bytes. */
123
+ fs_capacity_bytes?: number;
94
124
  }
95
125
  /**
96
126
  * Configuration options for the SandboxClient.
@@ -239,6 +269,11 @@ export interface RunOptions {
239
269
  * Options for creating a sandbox.
240
270
  */
241
271
  export interface CreateSandboxOptions {
272
+ /**
273
+ * Snapshot ID to boot from.
274
+ * Mutually exclusive with the `templateName` positional arg.
275
+ */
276
+ snapshotId?: string;
242
277
  /**
243
278
  * Optional sandbox name (auto-generated if not provided).
244
279
  */
@@ -264,6 +299,60 @@ export interface CreateSandboxOptions {
264
299
  * Must be a multiple of 60, or `0`/`undefined` to disable or omit.
265
300
  */
266
301
  idleTtlSeconds?: number;
302
+ /** Number of vCPUs. */
303
+ vCpus?: number;
304
+ /** Memory in bytes. */
305
+ memBytes?: number;
306
+ /** Root filesystem capacity in bytes. */
307
+ fsCapacityBytes?: number;
308
+ }
309
+ /**
310
+ * Options for creating a snapshot from a Docker image.
311
+ */
312
+ export interface CreateSnapshotOptions {
313
+ /** Private registry ID (alternative to URL/credentials). */
314
+ registryId?: string;
315
+ /** Registry URL for private images. */
316
+ registryUrl?: string;
317
+ /** Registry username. */
318
+ registryUsername?: string;
319
+ /** Registry password. */
320
+ registryPassword?: string;
321
+ /** Timeout in seconds when waiting for ready. Default: 60. */
322
+ timeout?: number;
323
+ /** AbortSignal for cancellation. */
324
+ signal?: AbortSignal;
325
+ }
326
+ /**
327
+ * Options for capturing a snapshot from a running sandbox.
328
+ */
329
+ export interface CaptureSnapshotOptions {
330
+ /** Checkpoint timestamp to use. If omitted, creates a fresh checkpoint. */
331
+ checkpoint?: string;
332
+ /** Timeout in seconds when waiting for ready. Default: 60. */
333
+ timeout?: number;
334
+ /** AbortSignal for cancellation. */
335
+ signal?: AbortSignal;
336
+ }
337
+ /**
338
+ * Options for waiting for a snapshot to become ready.
339
+ */
340
+ export interface WaitForSnapshotOptions {
341
+ /** Maximum time in seconds to wait. Default: 300. */
342
+ timeout?: number;
343
+ /** Time in seconds between status polls. Default: 2.0. */
344
+ pollInterval?: number;
345
+ /** AbortSignal for cancellation. */
346
+ signal?: AbortSignal;
347
+ }
348
+ /**
349
+ * Options for starting a stopped sandbox.
350
+ */
351
+ export interface StartSandboxOptions {
352
+ /** Timeout in seconds when waiting for ready. Default: 120. */
353
+ timeout?: number;
354
+ /** AbortSignal for cancellation. */
355
+ signal?: AbortSignal;
267
356
  }
268
357
  /**
269
358
  * Options for updating a sandbox (name and/or TTL).
@@ -295,6 +384,8 @@ export interface WaitForSandboxOptions {
295
384
  * Default: 1.0.
296
385
  */
297
386
  pollInterval?: number;
387
+ /** AbortSignal for cancellation. */
388
+ signal?: AbortSignal;
298
389
  }
299
390
  /**
300
391
  * Options for creating a volume.
package/dist/index.cjs CHANGED
@@ -18,4 +18,4 @@ Object.defineProperty(exports, "PromptCache", { enumerable: true, get: function
18
18
  Object.defineProperty(exports, "configureGlobalPromptCache", { enumerable: true, get: function () { return index_js_1.configureGlobalPromptCache; } });
19
19
  Object.defineProperty(exports, "promptCacheSingleton", { enumerable: true, get: function () { return index_js_1.promptCacheSingleton; } });
20
20
  // Update using pnpm bump-version
21
- exports.__version__ = "0.5.19";
21
+ exports.__version__ = "0.5.20";
package/dist/index.d.ts CHANGED
@@ -5,4 +5,4 @@ export { overrideFetchImplementation } from "./singletons/fetch.js";
5
5
  export { getDefaultProjectName } from "./utils/project.js";
6
6
  export { uuid7, uuid7FromTime } from "./uuid.js";
7
7
  export { Cache, PromptCache, type CacheConfig, type CacheMetrics, configureGlobalPromptCache, promptCacheSingleton, } from "./utils/prompt_cache/index.js";
8
- export declare const __version__ = "0.5.19";
8
+ export declare const __version__ = "0.5.20";
package/dist/index.js CHANGED
@@ -5,4 +5,4 @@ export { getDefaultProjectName } from "./utils/project.js";
5
5
  export { uuid7, uuid7FromTime } from "./uuid.js";
6
6
  export { Cache, PromptCache, configureGlobalPromptCache, promptCacheSingleton, } from "./utils/prompt_cache/index.js";
7
7
  // Update using pnpm bump-version
8
- export const __version__ = "0.5.19";
8
+ export const __version__ = "0.5.20";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langsmith",
3
- "version": "0.5.19",
3
+ "version": "0.5.20",
4
4
  "description": "Client library to connect to the LangSmith Observability and Evaluation Platform.",
5
5
  "packageManager": "pnpm@10.33.0",
6
6
  "files": [