langsmith 0.8.11 → 0.9.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.
Files changed (42) hide show
  1. package/dist/_openapi_client/resources/issues.d.ts +6 -0
  2. package/dist/_openapi_client/resources/online-evaluators.cjs +2 -2
  3. package/dist/_openapi_client/resources/online-evaluators.d.ts +7 -2
  4. package/dist/_openapi_client/resources/online-evaluators.js +2 -2
  5. package/dist/_openapi_client/resources/public/runs.d.ts +1 -1
  6. package/dist/_openapi_client/resources/sandboxes/boxes.cjs +2 -0
  7. package/dist/_openapi_client/resources/sandboxes/boxes.d.ts +25 -0
  8. package/dist/_openapi_client/resources/sandboxes/boxes.js +2 -0
  9. package/dist/_openapi_client/resources/sandboxes/sandboxes.d.ts +7 -2
  10. package/dist/_openapi_client/resources/sandboxes/sandboxes.js +1 -1
  11. package/dist/_openapi_client/resources/sandboxes/snapshots.cjs +12 -2
  12. package/dist/_openapi_client/resources/sandboxes/snapshots.d.ts +25 -3
  13. package/dist/_openapi_client/resources/sandboxes/snapshots.js +12 -2
  14. package/dist/_openapi_client/resources/threads.cjs +1 -1
  15. package/dist/_openapi_client/resources/threads.d.ts +26 -1
  16. package/dist/_openapi_client/resources/threads.js +1 -1
  17. package/dist/index.cjs +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/run_trees.cjs +19 -2
  21. package/dist/run_trees.d.ts +12 -0
  22. package/dist/run_trees.js +19 -2
  23. package/dist/sandbox/client.cjs +55 -7
  24. package/dist/sandbox/client.d.ts +27 -1
  25. package/dist/sandbox/client.js +55 -7
  26. package/dist/sandbox/errors.cjs +17 -6
  27. package/dist/sandbox/errors.d.ts +12 -5
  28. package/dist/sandbox/errors.js +15 -5
  29. package/dist/sandbox/index.cjs +2 -1
  30. package/dist/sandbox/index.d.ts +2 -2
  31. package/dist/sandbox/index.js +1 -1
  32. package/dist/sandbox/sandbox.cjs +27 -1
  33. package/dist/sandbox/sandbox.d.ts +25 -1
  34. package/dist/sandbox/sandbox.js +28 -2
  35. package/dist/sandbox/types.d.ts +35 -0
  36. package/dist/sandbox/ws_execute.cjs +7 -3
  37. package/dist/sandbox/ws_execute.js +8 -4
  38. package/dist/utils/env.cjs +18 -7
  39. package/dist/utils/env.js +18 -7
  40. package/dist/wrappers/openai_agents.cjs +2 -1
  41. package/dist/wrappers/openai_agents.js +2 -1
  42. package/package.json +1 -1
@@ -2,7 +2,7 @@
2
2
  * Main SandboxClient class for interacting with the sandbox server API.
3
3
  */
4
4
  import { Registries } from "../_openapi_client/resources/sandboxes/registries.js";
5
- import type { CaptureSnapshotOptions, CreateDockerfileSnapshotOptions, CreateSandboxOptions, CreateSnapshotOptions, ListSnapshotsOptions, ResourceStatus, SandboxClientConfig, Snapshot, StartSandboxOptions, UpdateSandboxOptions, WaitForSandboxOptions, WaitForSnapshotOptions } from "./types.js";
5
+ import type { CaptureSnapshotOptions, CreateDockerfileSnapshotOptions, CreateSandboxOptions, CreateSnapshotOptions, DownloadURL, GenerateDownloadURLOptions, ListSnapshotsOptions, ResourceStatus, SandboxClientConfig, Snapshot, StartSandboxOptions, UpdateSandboxOptions, WaitForSandboxOptions, WaitForSnapshotOptions } from "./types.js";
6
6
  import { Sandbox } from "./sandbox.js";
7
7
  /**
8
8
  * Client for interacting with the Sandbox Server API.
@@ -64,6 +64,7 @@ export declare class SandboxClient {
64
64
  * ```
65
65
  */
66
66
  get registries(): Registries;
67
+ private _postJson;
67
68
  /**
68
69
  * Create a new Sandbox.
69
70
  *
@@ -181,6 +182,31 @@ export declare class SandboxClient {
181
182
  * @returns Sandbox in "ready" status.
182
183
  */
183
184
  startSandbox(name: string, options?: StartSandboxOptions): Promise<Sandbox>;
185
+ /**
186
+ * Create a link that downloads one file from a sandbox.
187
+ *
188
+ * The link carries its own token, so anyone holding the URL can fetch that
189
+ * one file without a LangSmith credential. It is pinned to the sandbox and
190
+ * the exact path and cannot be repointed at another file. Fetching wakes a
191
+ * stopped sandbox.
192
+ *
193
+ * Do not modify the file after minting a link for it. The link is pinned to
194
+ * a path, not to a snapshot of the contents, so a later write to that path
195
+ * may or may not be reflected in what the link serves. Write a new file and
196
+ * mint a new link when the contents change.
197
+ *
198
+ * @param name - Sandbox name.
199
+ * @param path - File path inside the sandbox.
200
+ * @param options - Expiry and response header overrides.
201
+ * @returns The link and its expiry, if any.
202
+ *
203
+ * @example
204
+ * ```typescript
205
+ * const link = await client.generateDownloadURL("my-vm", "/tmp/report.pdf");
206
+ * console.log(link.download_url);
207
+ * ```
208
+ */
209
+ generateDownloadURL(name: string, path: string, options?: GenerateDownloadURLOptions): Promise<DownloadURL>;
184
210
  /**
185
211
  * Stop a running sandbox (preserves sandbox files for later restart).
186
212
  *
@@ -400,6 +400,10 @@ export class SandboxClient {
400
400
  * Callers can add specific status checks (e.g. 404) before calling this.
401
401
  * @internal
402
402
  */
403
+ _boxUrl(name, ...segments) {
404
+ const suffix = segments.length ? `/${segments.join("/")}` : "";
405
+ return `${this._baseUrl}/boxes/${encodeURIComponent(name)}${suffix}`;
406
+ }
403
407
  async _postJson(url, body, options) {
404
408
  const response = await this._fetch(url, {
405
409
  method: "POST",
@@ -484,7 +488,7 @@ export class SandboxClient {
484
488
  * @throws LangSmithResourceNotFoundError if sandbox not found.
485
489
  */
486
490
  async getSandbox(name, options) {
487
- const url = `${this._baseUrl}/boxes/${encodeURIComponent(name)}`;
491
+ const url = this._boxUrl(name);
488
492
  const response = await this._fetch(url, { signal: options?.signal });
489
493
  if (!response.ok) {
490
494
  if (response.status === 404) {
@@ -524,7 +528,7 @@ export class SandboxClient {
524
528
  deleteAfterStopSeconds === undefined) {
525
529
  return this.getSandbox(name);
526
530
  }
527
- const url = `${this._baseUrl}/boxes/${encodeURIComponent(name)}`;
531
+ const url = this._boxUrl(name);
528
532
  const payload = {};
529
533
  if (newName !== undefined) {
530
534
  payload.name = newName;
@@ -561,7 +565,7 @@ export class SandboxClient {
561
565
  * @throws LangSmithResourceNotFoundError if sandbox not found.
562
566
  */
563
567
  async deleteSandbox(name) {
564
- const url = `${this._baseUrl}/boxes/${encodeURIComponent(name)}`;
568
+ const url = this._boxUrl(name);
565
569
  const response = await this._fetch(url, { method: "DELETE" });
566
570
  if (!response.ok) {
567
571
  if (response.status === 404) {
@@ -581,7 +585,7 @@ export class SandboxClient {
581
585
  * @throws LangSmithResourceNotFoundError if sandbox not found.
582
586
  */
583
587
  async getSandboxStatus(name, options) {
584
- const url = `${this._baseUrl}/boxes/${encodeURIComponent(name)}/status`;
588
+ const url = this._boxUrl(name, "status");
585
589
  const response = await this._fetch(url, { signal: options?.signal });
586
590
  if (!response.ok) {
587
591
  if (response.status === 404) {
@@ -644,17 +648,61 @@ export class SandboxClient {
644
648
  */
645
649
  async startSandbox(name, options = {}) {
646
650
  const { timeout = 120, signal } = options;
647
- const url = `${this._baseUrl}/boxes/${encodeURIComponent(name)}/start`;
651
+ const url = this._boxUrl(name, "start");
648
652
  await this._postJson(url, {}, { signal });
649
653
  return this.waitForSandbox(name, { timeout, signal });
650
654
  }
655
+ /**
656
+ * Create a link that downloads one file from a sandbox.
657
+ *
658
+ * The link carries its own token, so anyone holding the URL can fetch that
659
+ * one file without a LangSmith credential. It is pinned to the sandbox and
660
+ * the exact path and cannot be repointed at another file. Fetching wakes a
661
+ * stopped sandbox.
662
+ *
663
+ * Do not modify the file after minting a link for it. The link is pinned to
664
+ * a path, not to a snapshot of the contents, so a later write to that path
665
+ * may or may not be reflected in what the link serves. Write a new file and
666
+ * mint a new link when the contents change.
667
+ *
668
+ * @param name - Sandbox name.
669
+ * @param path - File path inside the sandbox.
670
+ * @param options - Expiry and response header overrides.
671
+ * @returns The link and its expiry, if any.
672
+ *
673
+ * @example
674
+ * ```typescript
675
+ * const link = await client.generateDownloadURL("my-vm", "/tmp/report.pdf");
676
+ * console.log(link.download_url);
677
+ * ```
678
+ */
679
+ async generateDownloadURL(name, path, options = {}) {
680
+ const { expiresInSeconds, contentType, contentDisposition, signal } = options;
681
+ if (expiresInSeconds !== undefined && expiresInSeconds < 1) {
682
+ throw new LangSmithValidationError(`expiresInSeconds must be greater than 0 (got ${expiresInSeconds}); ` +
683
+ `omit it for a link that never expires`);
684
+ }
685
+ const url = this._boxUrl(name, "download-url");
686
+ const payload = { path };
687
+ if (expiresInSeconds !== undefined) {
688
+ payload.expires_in_seconds = expiresInSeconds;
689
+ }
690
+ if (contentType !== undefined) {
691
+ payload.content_type = contentType;
692
+ }
693
+ if (contentDisposition !== undefined) {
694
+ payload.content_disposition = contentDisposition;
695
+ }
696
+ const response = await this._postJson(url, payload, { signal });
697
+ return (await response.json());
698
+ }
651
699
  /**
652
700
  * Stop a running sandbox (preserves sandbox files for later restart).
653
701
  *
654
702
  * @param name - Sandbox name.
655
703
  */
656
704
  async stopSandbox(name) {
657
- const url = `${this._baseUrl}/boxes/${encodeURIComponent(name)}/stop`;
705
+ const url = this._boxUrl(name, "stop");
658
706
  await this._postJson(url, {});
659
707
  }
660
708
  // =========================================================================
@@ -775,7 +823,7 @@ export class SandboxClient {
775
823
  */
776
824
  async captureSnapshot(sandboxName, name, options = {}) {
777
825
  const { dockerImage, fsCapacityBytes, timeout = 60, signal } = options;
778
- const url = `${this._baseUrl}/boxes/${encodeURIComponent(sandboxName)}/snapshot`;
826
+ const url = this._boxUrl(sandboxName, "snapshot");
779
827
  const payload = { name };
780
828
  if (dockerImage !== undefined) {
781
829
  payload.docker_image = dockerImage;
@@ -7,7 +7,7 @@
7
7
  * properties for specific handling when needed.
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.LangSmithSandboxServerReloadError = exports.LangSmithSandboxConnectTimeoutError = exports.LangSmithStreamEndedBeforeStartedError = exports.LangSmithCommandTimeoutError = exports.LangSmithSandboxOperationError = exports.LangSmithSandboxNotReadyError = exports.LangSmithDataplaneNotConfiguredError = exports.LangSmithSandboxCreationError = exports.LangSmithResourceCreationError = exports.LangSmithQuotaExceededError = exports.LangSmithValidationError = exports.LangSmithResourceNameConflictError = exports.LangSmithResourceAlreadyExistsError = exports.LangSmithResourceInUseError = exports.LangSmithResourceTimeoutError = exports.LangSmithResourceNotFoundError = exports.LangSmithSandboxConnectionError = exports.LangSmithSandboxAuthenticationError = exports.LangSmithSandboxAPIError = exports.LangSmithSandboxError = void 0;
10
+ exports.LangSmithSandboxServerReloadError = exports.LangSmithSandboxConnectTimeoutError = exports.LangSmithStreamEndedBeforeStartedError = exports.LangSmithCommandTimeoutError = exports.LangSmithSandboxOperationError = exports.LangSmithSandboxNotReadyError = exports.LangSmithDataplaneNotConfiguredError = exports.LangSmithSandboxCreationError = exports.LangSmithResourceCreationError = exports.LangSmithQuotaExceededError = exports.LangSmithValidationError = exports.LangSmithResourceNameConflictError = exports.LangSmithResourceAlreadyExistsError = exports.LangSmithResourceInUseError = exports.LangSmithResourceTimeoutError = exports.LangSmithResourceNotFoundError = exports.LangSmithSandboxRetryableConnectionError = exports.LangSmithSandboxConnectionError = exports.LangSmithSandboxAuthenticationError = exports.LangSmithSandboxAPIError = exports.LangSmithSandboxError = void 0;
11
11
  /**
12
12
  * Base exception for sandbox client errors.
13
13
  */
@@ -53,6 +53,19 @@ class LangSmithSandboxConnectionError extends LangSmithSandboxError {
53
53
  }
54
54
  }
55
55
  exports.LangSmithSandboxConnectionError = LangSmithSandboxConnectionError;
56
+ /**
57
+ * Raised when a transient connection failure occurs before a command starts.
58
+ *
59
+ * `run()` retries this error with the same command ID so the server can
60
+ * deduplicate an attempt whose outcome is unknown.
61
+ */
62
+ class LangSmithSandboxRetryableConnectionError extends LangSmithSandboxConnectionError {
63
+ constructor(message) {
64
+ super(message);
65
+ this.name = "LangSmithSandboxRetryableConnectionError";
66
+ }
67
+ }
68
+ exports.LangSmithSandboxRetryableConnectionError = LangSmithSandboxRetryableConnectionError;
56
69
  // =============================================================================
57
70
  // Resource Errors (type-based, with resourceType property)
58
71
  // =============================================================================
@@ -375,12 +388,10 @@ exports.LangSmithStreamEndedBeforeStartedError = LangSmithStreamEndedBeforeStart
375
388
  /**
376
389
  * Thrown when the socket fails or times out before the WebSocket handshake.
377
390
  *
378
- * Distinct from its parent because it is safely retryable: the execute frame
379
- * was never sent, so re-issuing the same command_id cannot double-run a
380
- * command. run() retries this with backoff; a plain connection error (a
381
- * rejected handshake) is permanent and propagates immediately.
391
+ * The execute frame was never sent, so re-issuing the same command ID cannot
392
+ * double-run a command.
382
393
  */
383
- class LangSmithSandboxConnectTimeoutError extends LangSmithSandboxConnectionError {
394
+ class LangSmithSandboxConnectTimeoutError extends LangSmithSandboxRetryableConnectionError {
384
395
  constructor(message) {
385
396
  super(message);
386
397
  this.name = "LangSmithSandboxConnectTimeoutError";
@@ -31,6 +31,15 @@ export declare class LangSmithSandboxAuthenticationError extends LangSmithSandbo
31
31
  export declare class LangSmithSandboxConnectionError extends LangSmithSandboxError {
32
32
  constructor(message: string);
33
33
  }
34
+ /**
35
+ * Raised when a transient connection failure occurs before a command starts.
36
+ *
37
+ * `run()` retries this error with the same command ID so the server can
38
+ * deduplicate an attempt whose outcome is unknown.
39
+ */
40
+ export declare class LangSmithSandboxRetryableConnectionError extends LangSmithSandboxConnectionError {
41
+ constructor(message: string);
42
+ }
34
43
  /**
35
44
  * Raised when a resource is not found.
36
45
  */
@@ -168,12 +177,10 @@ export declare class LangSmithStreamEndedBeforeStartedError extends LangSmithSan
168
177
  /**
169
178
  * Thrown when the socket fails or times out before the WebSocket handshake.
170
179
  *
171
- * Distinct from its parent because it is safely retryable: the execute frame
172
- * was never sent, so re-issuing the same command_id cannot double-run a
173
- * command. run() retries this with backoff; a plain connection error (a
174
- * rejected handshake) is permanent and propagates immediately.
180
+ * The execute frame was never sent, so re-issuing the same command ID cannot
181
+ * double-run a command.
175
182
  */
176
- export declare class LangSmithSandboxConnectTimeoutError extends LangSmithSandboxConnectionError {
183
+ export declare class LangSmithSandboxConnectTimeoutError extends LangSmithSandboxRetryableConnectionError {
177
184
  constructor(message: string);
178
185
  }
179
186
  /**
@@ -46,6 +46,18 @@ export class LangSmithSandboxConnectionError extends LangSmithSandboxError {
46
46
  this.name = "LangSmithSandboxConnectionError";
47
47
  }
48
48
  }
49
+ /**
50
+ * Raised when a transient connection failure occurs before a command starts.
51
+ *
52
+ * `run()` retries this error with the same command ID so the server can
53
+ * deduplicate an attempt whose outcome is unknown.
54
+ */
55
+ export class LangSmithSandboxRetryableConnectionError extends LangSmithSandboxConnectionError {
56
+ constructor(message) {
57
+ super(message);
58
+ this.name = "LangSmithSandboxRetryableConnectionError";
59
+ }
60
+ }
49
61
  // =============================================================================
50
62
  // Resource Errors (type-based, with resourceType property)
51
63
  // =============================================================================
@@ -354,12 +366,10 @@ export class LangSmithStreamEndedBeforeStartedError extends LangSmithSandboxOper
354
366
  /**
355
367
  * Thrown when the socket fails or times out before the WebSocket handshake.
356
368
  *
357
- * Distinct from its parent because it is safely retryable: the execute frame
358
- * was never sent, so re-issuing the same command_id cannot double-run a
359
- * command. run() retries this with backoff; a plain connection error (a
360
- * rejected handshake) is permanent and propagates immediately.
369
+ * The execute frame was never sent, so re-issuing the same command ID cannot
370
+ * double-run a command.
361
371
  */
362
- export class LangSmithSandboxConnectTimeoutError extends LangSmithSandboxConnectionError {
372
+ export class LangSmithSandboxConnectTimeoutError extends LangSmithSandboxRetryableConnectionError {
363
373
  constructor(message) {
364
374
  super(message);
365
375
  this.name = "LangSmithSandboxConnectTimeoutError";
@@ -29,7 +29,7 @@
29
29
  * @packageDocumentation
30
30
  */
31
31
  Object.defineProperty(exports, "__esModule", { value: true });
32
- exports.LangSmithDataplaneNotConfiguredError = exports.LangSmithCommandTimeoutError = exports.LangSmithSandboxOperationError = exports.LangSmithSandboxNotReadyError = exports.LangSmithSandboxCreationError = exports.LangSmithResourceCreationError = exports.LangSmithQuotaExceededError = exports.LangSmithValidationError = exports.LangSmithResourceNameConflictError = exports.LangSmithResourceAlreadyExistsError = exports.LangSmithResourceInUseError = exports.LangSmithResourceTimeoutError = exports.LangSmithResourceNotFoundError = exports.LangSmithSandboxServerReloadError = exports.LangSmithSandboxConnectTimeoutError = exports.LangSmithSandboxConnectionError = exports.LangSmithSandboxAuthenticationError = exports.LangSmithSandboxAPIError = exports.LangSmithSandboxError = exports.s3Mount = exports.mountConfig = exports.gitMount = exports.gcsMount = exports.contextHubMount = exports.workspaceSecret = exports.proxyConfig = exports.opaqueSecret = exports.gcpAuth = exports.awsAuth = exports.CommandHandle = exports.Sandbox = exports.SandboxClient = void 0;
32
+ exports.LangSmithDataplaneNotConfiguredError = exports.LangSmithCommandTimeoutError = exports.LangSmithSandboxOperationError = exports.LangSmithSandboxNotReadyError = exports.LangSmithSandboxCreationError = exports.LangSmithResourceCreationError = exports.LangSmithQuotaExceededError = exports.LangSmithValidationError = exports.LangSmithResourceNameConflictError = exports.LangSmithResourceAlreadyExistsError = exports.LangSmithResourceInUseError = exports.LangSmithResourceTimeoutError = exports.LangSmithResourceNotFoundError = exports.LangSmithSandboxServerReloadError = exports.LangSmithSandboxConnectTimeoutError = exports.LangSmithSandboxRetryableConnectionError = exports.LangSmithSandboxConnectionError = exports.LangSmithSandboxAuthenticationError = exports.LangSmithSandboxAPIError = exports.LangSmithSandboxError = exports.s3Mount = exports.mountConfig = exports.gitMount = exports.gcsMount = exports.contextHubMount = exports.workspaceSecret = exports.proxyConfig = exports.opaqueSecret = exports.gcpAuth = exports.awsAuth = exports.CommandHandle = exports.Sandbox = exports.SandboxClient = void 0;
33
33
  // Main classes
34
34
  var client_js_1 = require("./client.cjs");
35
35
  Object.defineProperty(exports, "SandboxClient", { enumerable: true, get: function () { return client_js_1.SandboxClient; } });
@@ -56,6 +56,7 @@ Object.defineProperty(exports, "LangSmithSandboxError", { enumerable: true, get:
56
56
  Object.defineProperty(exports, "LangSmithSandboxAPIError", { enumerable: true, get: function () { return errors_js_1.LangSmithSandboxAPIError; } });
57
57
  Object.defineProperty(exports, "LangSmithSandboxAuthenticationError", { enumerable: true, get: function () { return errors_js_1.LangSmithSandboxAuthenticationError; } });
58
58
  Object.defineProperty(exports, "LangSmithSandboxConnectionError", { enumerable: true, get: function () { return errors_js_1.LangSmithSandboxConnectionError; } });
59
+ Object.defineProperty(exports, "LangSmithSandboxRetryableConnectionError", { enumerable: true, get: function () { return errors_js_1.LangSmithSandboxRetryableConnectionError; } });
59
60
  Object.defineProperty(exports, "LangSmithSandboxConnectTimeoutError", { enumerable: true, get: function () { return errors_js_1.LangSmithSandboxConnectTimeoutError; } });
60
61
  Object.defineProperty(exports, "LangSmithSandboxServerReloadError", { enumerable: true, get: function () { return errors_js_1.LangSmithSandboxServerReloadError; } });
61
62
  // Resource errors (type-based with resourceType attribute)
@@ -32,5 +32,5 @@ export { Sandbox } from "./sandbox.js";
32
32
  export { CommandHandle } from "./command_handle.js";
33
33
  export { awsAuth, gcpAuth, opaqueSecret, proxyConfig, workspaceSecret, } from "./proxy_config.js";
34
34
  export { contextHubMount, gcsMount, gitMount, mountConfig, s3Mount, } from "./mounts.js";
35
- export type { ExecutionResult, OutputChunk, WsMessage, WsRunOptions, ResourceStatus, Snapshot, SandboxData, SandboxClientConfig, RunOptions, CreateSandboxOptions, SandboxAccessControl, SandboxAwsAuthRule, SandboxAwsMountAuthConfig, SandboxGcpAuthRule, SandboxGcpMountAuthConfig, SandboxMountAuth, SandboxMountAuthConfig, SandboxMountConfig, SandboxProxyConfig, SandboxProxyRule, SandboxProxySecret, SandboxMount, MountCacheConfig, ContextHubMountConfig, ContextHubMountSpec, GCSMountConfig, GCSMountSpec, GitMountConfig, GitMountRefSpec, GitMountSpec, S3MountConfig, S3MountSpec, CreateSnapshotOptions, CreateDockerfileSnapshotOptions, CaptureSnapshotOptions, ListSnapshotsOptions, WaitForSnapshotOptions, StartSandboxOptions, UpdateSandboxOptions, WaitForSandboxOptions, } from "./types.js";
36
- export { LangSmithSandboxError, LangSmithSandboxAPIError, LangSmithSandboxAuthenticationError, LangSmithSandboxConnectionError, LangSmithSandboxConnectTimeoutError, LangSmithSandboxServerReloadError, LangSmithResourceNotFoundError, LangSmithResourceTimeoutError, LangSmithResourceInUseError, LangSmithResourceAlreadyExistsError, LangSmithResourceNameConflictError, LangSmithValidationError, LangSmithQuotaExceededError, LangSmithResourceCreationError, LangSmithSandboxCreationError, LangSmithSandboxNotReadyError, LangSmithSandboxOperationError, LangSmithCommandTimeoutError, LangSmithDataplaneNotConfiguredError, } from "./errors.js";
35
+ export type { ExecutionResult, OutputChunk, WsMessage, WsRunOptions, ResourceStatus, Snapshot, SandboxData, SandboxClientConfig, RunOptions, CreateSandboxOptions, SandboxAccessControl, SandboxAwsAuthRule, SandboxAwsMountAuthConfig, SandboxGcpAuthRule, SandboxGcpMountAuthConfig, SandboxMountAuth, SandboxMountAuthConfig, SandboxMountConfig, SandboxProxyConfig, SandboxProxyRule, SandboxProxySecret, SandboxMount, MountCacheConfig, ContextHubMountConfig, ContextHubMountSpec, GCSMountConfig, GCSMountSpec, GitMountConfig, GitMountRefSpec, GitMountSpec, S3MountConfig, S3MountSpec, CreateSnapshotOptions, CreateDockerfileSnapshotOptions, CaptureSnapshotOptions, DownloadContentDisposition, DownloadURL, GenerateDownloadURLOptions, ListSnapshotsOptions, WaitForSnapshotOptions, StartSandboxOptions, UpdateSandboxOptions, WaitForSandboxOptions, } from "./types.js";
36
+ export { LangSmithSandboxError, LangSmithSandboxAPIError, LangSmithSandboxAuthenticationError, LangSmithSandboxConnectionError, LangSmithSandboxRetryableConnectionError, LangSmithSandboxConnectTimeoutError, LangSmithSandboxServerReloadError, LangSmithResourceNotFoundError, LangSmithResourceTimeoutError, LangSmithResourceInUseError, LangSmithResourceAlreadyExistsError, LangSmithResourceNameConflictError, LangSmithValidationError, LangSmithQuotaExceededError, LangSmithResourceCreationError, LangSmithSandboxCreationError, LangSmithSandboxNotReadyError, LangSmithSandboxOperationError, LangSmithCommandTimeoutError, LangSmithDataplaneNotConfiguredError, } from "./errors.js";
@@ -36,7 +36,7 @@ export { contextHubMount, gcsMount, gitMount, mountConfig, s3Mount, } from "./mo
36
36
  // Errors
37
37
  export {
38
38
  // Base and connection errors
39
- LangSmithSandboxError, LangSmithSandboxAPIError, LangSmithSandboxAuthenticationError, LangSmithSandboxConnectionError, LangSmithSandboxConnectTimeoutError, LangSmithSandboxServerReloadError,
39
+ LangSmithSandboxError, LangSmithSandboxAPIError, LangSmithSandboxAuthenticationError, LangSmithSandboxConnectionError, LangSmithSandboxRetryableConnectionError, LangSmithSandboxConnectTimeoutError, LangSmithSandboxServerReloadError,
40
40
  // Resource errors (type-based with resourceType attribute)
41
41
  LangSmithResourceNotFoundError, LangSmithResourceTimeoutError, LangSmithResourceInUseError, LangSmithResourceAlreadyExistsError, LangSmithResourceNameConflictError,
42
42
  // Validation and quota errors
@@ -264,7 +264,7 @@ class Sandbox {
264
264
  // Idempotent re-issue (same command_id): neither an early close nor a
265
265
  // failed connect can have started a second command.
266
266
  if (!(e instanceof errors_js_1.LangSmithStreamEndedBeforeStartedError) &&
267
- !(e instanceof errors_js_1.LangSmithSandboxConnectTimeoutError)) {
267
+ !(e instanceof errors_js_1.LangSmithSandboxRetryableConnectionError)) {
268
268
  throw e;
269
269
  }
270
270
  attempt++;
@@ -410,6 +410,32 @@ class Sandbox {
410
410
  const buffer = await response.arrayBuffer();
411
411
  return new Uint8Array(buffer);
412
412
  }
413
+ /**
414
+ * Create a link that downloads one file from this sandbox.
415
+ *
416
+ * The link carries its own token, so anyone holding the URL can fetch that
417
+ * one file without a LangSmith credential. Fetching wakes a stopped sandbox.
418
+ *
419
+ * Do not modify the file after minting a link for it. The link is pinned to
420
+ * a path, not to a snapshot of the contents, so a later write to that path
421
+ * may or may not be reflected in what the link serves. Write a new file and
422
+ * mint a new link when the contents change.
423
+ *
424
+ * @param path - File path inside the sandbox.
425
+ * @param options - Expiry and response header overrides.
426
+ * @returns The link and its expiry, if any.
427
+ *
428
+ * @example
429
+ * ```typescript
430
+ * const link = await sandbox.generateDownloadURL("/tmp/report.pdf", {
431
+ * expiresInSeconds: 3600,
432
+ * });
433
+ * console.log(link.download_url);
434
+ * ```
435
+ */
436
+ async generateDownloadURL(path, options = {}) {
437
+ return this._client.generateDownloadURL(this.name, path, options);
438
+ }
413
439
  /**
414
440
  * Delete this sandbox.
415
441
  *
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Sandbox class for interacting with a specific sandbox instance.
3
3
  */
4
- import type { CaptureSnapshotOptions, ExecutionResult, RunOptions, Snapshot, StartSandboxOptions } from "./types.js";
4
+ import type { CaptureSnapshotOptions, DownloadURL, ExecutionResult, GenerateDownloadURLOptions, 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.
@@ -155,6 +155,30 @@ export declare class Sandbox {
155
155
  * ```
156
156
  */
157
157
  read(path: string, timeout?: number): Promise<Uint8Array>;
158
+ /**
159
+ * Create a link that downloads one file from this sandbox.
160
+ *
161
+ * The link carries its own token, so anyone holding the URL can fetch that
162
+ * one file without a LangSmith credential. Fetching wakes a stopped sandbox.
163
+ *
164
+ * Do not modify the file after minting a link for it. The link is pinned to
165
+ * a path, not to a snapshot of the contents, so a later write to that path
166
+ * may or may not be reflected in what the link serves. Write a new file and
167
+ * mint a new link when the contents change.
168
+ *
169
+ * @param path - File path inside the sandbox.
170
+ * @param options - Expiry and response header overrides.
171
+ * @returns The link and its expiry, if any.
172
+ *
173
+ * @example
174
+ * ```typescript
175
+ * const link = await sandbox.generateDownloadURL("/tmp/report.pdf", {
176
+ * expiresInSeconds: 3600,
177
+ * });
178
+ * console.log(link.download_url);
179
+ * ```
180
+ */
181
+ generateDownloadURL(path: string, options?: GenerateDownloadURLOptions): Promise<DownloadURL>;
158
182
  /**
159
183
  * Delete this sandbox.
160
184
  *
@@ -2,7 +2,7 @@
2
2
  * Sandbox class for interacting with a specific sandbox instance.
3
3
  */
4
4
  import { uuid7 } from "../uuid.js";
5
- import { LangSmithDataplaneNotConfiguredError, LangSmithSandboxConnectTimeoutError, LangSmithStreamEndedBeforeStartedError, } from "./errors.js";
5
+ import { LangSmithDataplaneNotConfiguredError, LangSmithSandboxRetryableConnectionError, LangSmithStreamEndedBeforeStartedError, } from "./errors.js";
6
6
  import { handleSandboxHttpError } from "./helpers.js";
7
7
  import { CommandHandle } from "./command_handle.js";
8
8
  import { connectDeadline, isWsAvailable, openTimeoutFor, reconnectWsStream, remainingBudget, runWsStream, } from "./ws_execute.js";
@@ -261,7 +261,7 @@ export class Sandbox {
261
261
  // Idempotent re-issue (same command_id): neither an early close nor a
262
262
  // failed connect can have started a second command.
263
263
  if (!(e instanceof LangSmithStreamEndedBeforeStartedError) &&
264
- !(e instanceof LangSmithSandboxConnectTimeoutError)) {
264
+ !(e instanceof LangSmithSandboxRetryableConnectionError)) {
265
265
  throw e;
266
266
  }
267
267
  attempt++;
@@ -407,6 +407,32 @@ export class Sandbox {
407
407
  const buffer = await response.arrayBuffer();
408
408
  return new Uint8Array(buffer);
409
409
  }
410
+ /**
411
+ * Create a link that downloads one file from this sandbox.
412
+ *
413
+ * The link carries its own token, so anyone holding the URL can fetch that
414
+ * one file without a LangSmith credential. Fetching wakes a stopped sandbox.
415
+ *
416
+ * Do not modify the file after minting a link for it. The link is pinned to
417
+ * a path, not to a snapshot of the contents, so a later write to that path
418
+ * may or may not be reflected in what the link serves. Write a new file and
419
+ * mint a new link when the contents change.
420
+ *
421
+ * @param path - File path inside the sandbox.
422
+ * @param options - Expiry and response header overrides.
423
+ * @returns The link and its expiry, if any.
424
+ *
425
+ * @example
426
+ * ```typescript
427
+ * const link = await sandbox.generateDownloadURL("/tmp/report.pdf", {
428
+ * expiresInSeconds: 3600,
429
+ * });
430
+ * console.log(link.download_url);
431
+ * ```
432
+ */
433
+ async generateDownloadURL(path, options = {}) {
434
+ return this._client.generateDownloadURL(this.name, path, options);
435
+ }
410
436
  /**
411
437
  * Delete this sandbox.
412
438
  *
@@ -559,6 +559,41 @@ export interface CaptureSnapshotOptions {
559
559
  /** AbortSignal for cancellation. */
560
560
  signal?: AbortSignal;
561
561
  }
562
+ /**
563
+ * How a download link asks the browser to handle the file.
564
+ */
565
+ export type DownloadContentDisposition = "attachment" | "inline";
566
+ /**
567
+ * Options for minting a sandbox file download link.
568
+ */
569
+ export interface GenerateDownloadURLOptions {
570
+ /**
571
+ * Link TTL in seconds. Omit for a link that never expires.
572
+ */
573
+ expiresInSeconds?: number;
574
+ /** Content-Type to serve the file as. */
575
+ contentType?: string;
576
+ /** Content-Disposition to serve the file with. */
577
+ contentDisposition?: DownloadContentDisposition;
578
+ /** AbortSignal for cancellation. */
579
+ signal?: AbortSignal;
580
+ }
581
+ /**
582
+ * A link that downloads one sandbox file with no LangSmith credential.
583
+ *
584
+ * The link is pinned to the sandbox, the file path, and the response headers,
585
+ * so it cannot be repointed at another file. It is pinned to the path rather
586
+ * than to a snapshot of the contents, so the file must not be modified while
587
+ * the link is in use.
588
+ */
589
+ export interface DownloadURL {
590
+ /** The full URL to fetch. Supports GET, HEAD, and Range. */
591
+ download_url: string;
592
+ /** The signed token embedded in `download_url`. */
593
+ token: string;
594
+ /** Expiry timestamp, or null for a link that never expires. */
595
+ expires_at: string | null;
596
+ }
562
597
  /**
563
598
  * Options for listing snapshots. All fields are optional and independent.
564
599
  *
@@ -35,6 +35,7 @@ exports.WS_OPEN_TIMEOUT = envTimeout("SANDBOX_WS_TIMEOUT_OPEN", 30);
35
35
  // Ceiling on the whole connect phase. Without it, retrying a blackholed
36
36
  // handshake costs MAX_AUTO_RECONNECTS + 1 full open timeouts plus backoff.
37
37
  exports.WS_CONNECT_BUDGET = envTimeout("SANDBOX_WS_TIMEOUT_CONNECT_BUDGET", 120);
38
+ const RETRYABLE_HANDSHAKE_STATUS_CODES = new Set([500, 502, 503, 504]);
38
39
  function nowSeconds() {
39
40
  return performance.now() / 1000;
40
41
  }
@@ -184,8 +185,8 @@ function raiseForWsError(msg, commandId = "") {
184
185
  * Create a ws WebSocket connection and return a promise that resolves when open
185
186
  * or rejects on error.
186
187
  *
187
- * A socket-level failure rejects with the retryable connect-timeout error; a
188
- * non-101 response rejects with the permanent connection error.
188
+ * Socket-level failures and transient 5xx responses are retryable. Other
189
+ * non-101 responses are permanent connection errors.
189
190
  */
190
191
  async function connectWs(url, headers, openTimeout = exports.WS_OPEN_TIMEOUT) {
191
192
  const { WebSocket: WS } = await ensureWs();
@@ -210,7 +211,10 @@ async function connectWs(url, headers, openTimeout = exports.WS_OPEN_TIMEOUT) {
210
211
  if (settled)
211
212
  return;
212
213
  settled = true;
213
- reject(new errors_js_1.LangSmithSandboxConnectionError(`WebSocket upgrade to ${url} rejected by server (HTTP ${res.statusCode})`));
214
+ const ErrorType = RETRYABLE_HANDSHAKE_STATUS_CODES.has(res.statusCode ?? 0)
215
+ ? errors_js_1.LangSmithSandboxRetryableConnectionError
216
+ : errors_js_1.LangSmithSandboxConnectionError;
217
+ reject(new ErrorType(`WebSocket upgrade to ${url} rejected by server (HTTP ${res.statusCode})`));
214
218
  });
215
219
  // Stays attached after settling so a late error has a listener and cannot
216
220
  // become an unhandled 'error' event.
@@ -4,7 +4,7 @@
4
4
  * Uses the `ws` npm package (optional peer dependency).
5
5
  * Install with: npm install ws
6
6
  */
7
- import { LangSmithCommandTimeoutError, LangSmithSandboxConnectionError, LangSmithSandboxConnectTimeoutError, LangSmithSandboxOperationError, LangSmithSandboxServerReloadError, } from "./errors.js";
7
+ import { LangSmithCommandTimeoutError, LangSmithSandboxConnectionError, LangSmithSandboxConnectTimeoutError, LangSmithSandboxOperationError, LangSmithSandboxRetryableConnectionError, LangSmithSandboxServerReloadError, } from "./errors.js";
8
8
  import { getLangSmithEnvironmentVariable } from "../utils/env.js";
9
9
  /** Read a WebSocket timeout override, in seconds. `<= 0` disables it. */
10
10
  function envTimeout(name, defaultSeconds) {
@@ -23,6 +23,7 @@ export const WS_OPEN_TIMEOUT = envTimeout("SANDBOX_WS_TIMEOUT_OPEN", 30);
23
23
  // Ceiling on the whole connect phase. Without it, retrying a blackholed
24
24
  // handshake costs MAX_AUTO_RECONNECTS + 1 full open timeouts plus backoff.
25
25
  export const WS_CONNECT_BUDGET = envTimeout("SANDBOX_WS_TIMEOUT_CONNECT_BUDGET", 120);
26
+ const RETRYABLE_HANDSHAKE_STATUS_CODES = new Set([500, 502, 503, 504]);
26
27
  function nowSeconds() {
27
28
  return performance.now() / 1000;
28
29
  }
@@ -171,8 +172,8 @@ export function raiseForWsError(msg, commandId = "") {
171
172
  * Create a ws WebSocket connection and return a promise that resolves when open
172
173
  * or rejects on error.
173
174
  *
174
- * A socket-level failure rejects with the retryable connect-timeout error; a
175
- * non-101 response rejects with the permanent connection error.
175
+ * Socket-level failures and transient 5xx responses are retryable. Other
176
+ * non-101 responses are permanent connection errors.
176
177
  */
177
178
  async function connectWs(url, headers, openTimeout = WS_OPEN_TIMEOUT) {
178
179
  const { WebSocket: WS } = await ensureWs();
@@ -197,7 +198,10 @@ async function connectWs(url, headers, openTimeout = WS_OPEN_TIMEOUT) {
197
198
  if (settled)
198
199
  return;
199
200
  settled = true;
200
- reject(new LangSmithSandboxConnectionError(`WebSocket upgrade to ${url} rejected by server (HTTP ${res.statusCode})`));
201
+ const ErrorType = RETRYABLE_HANDSHAKE_STATUS_CODES.has(res.statusCode ?? 0)
202
+ ? LangSmithSandboxRetryableConnectionError
203
+ : LangSmithSandboxConnectionError;
204
+ reject(new ErrorType(`WebSocket upgrade to ${url} rejected by server (HTTP ${res.statusCode})`));
201
205
  });
202
206
  // Stays attached after settling so a late error has a listener and cannot
203
207
  // become an unhandled 'error' event.
@@ -76,6 +76,22 @@ function getRuntimeEnvironment() {
76
76
  }
77
77
  return runtimeEnvironment;
78
78
  }
79
+ // Substrings marking a variable as secret-bearing or personal; matched
80
+ // case-insensitively against the variable name.
81
+ const EXCLUDED_SUBSTRINGS = [
82
+ "key",
83
+ "secret",
84
+ "token",
85
+ "password",
86
+ "passwd",
87
+ "pwd",
88
+ "credential",
89
+ "email",
90
+ ];
91
+ function isSensitiveEnvVarName(key) {
92
+ const lowered = key.toLowerCase();
93
+ return EXCLUDED_SUBSTRINGS.some((sub) => lowered.includes(sub));
94
+ }
79
95
  /**
80
96
  * Retrieves the LangSmith-specific metadata from the current runtime environment.
81
97
  *
@@ -101,9 +117,7 @@ function getLangSmithEnvVarsMetadata() {
101
117
  for (const [key, value] of Object.entries(allEnvVars)) {
102
118
  if (typeof value === "string" &&
103
119
  !excluded.includes(key) &&
104
- !key.toLowerCase().includes("key") &&
105
- !key.toLowerCase().includes("secret") &&
106
- !key.toLowerCase().includes("token")) {
120
+ !isSensitiveEnvVarName(key)) {
107
121
  if (key === "LANGCHAIN_REVISION_ID") {
108
122
  envVars["revision_id"] = value;
109
123
  }
@@ -131,10 +145,7 @@ function getLangSmithEnvironmentVariables() {
131
145
  for (const [key, value] of Object.entries(process.env)) {
132
146
  if ((key.startsWith("LANGCHAIN_") || key.startsWith("LANGSMITH_")) &&
133
147
  value != null) {
134
- if ((key.toLowerCase().includes("key") ||
135
- key.toLowerCase().includes("secret") ||
136
- key.toLowerCase().includes("token")) &&
137
- typeof value === "string") {
148
+ if (isSensitiveEnvVarName(key) && typeof value === "string") {
138
149
  envVars[key] =
139
150
  value.slice(0, 2) +
140
151
  "*".repeat(value.length - 4) +