langsmith 0.10.3 → 0.10.5

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 (46) hide show
  1. package/dist/client.cjs +19 -4
  2. package/dist/client.d.ts +2 -1
  3. package/dist/client.js +19 -4
  4. package/dist/evaluation/_runner.cjs +47 -5
  5. package/dist/evaluation/_runner.d.ts +12 -0
  6. package/dist/evaluation/_runner.js +47 -5
  7. package/dist/index.cjs +1 -1
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.js +1 -1
  10. package/dist/sandbox/access_delegation.cjs +24 -0
  11. package/dist/sandbox/access_delegation.d.ts +15 -0
  12. package/dist/sandbox/access_delegation.js +21 -0
  13. package/dist/sandbox/client.cjs +70 -5
  14. package/dist/sandbox/client.d.ts +18 -0
  15. package/dist/sandbox/client.js +70 -5
  16. package/dist/sandbox/command_handle.cjs +34 -0
  17. package/dist/sandbox/command_handle.d.ts +10 -0
  18. package/dist/sandbox/command_handle.js +34 -0
  19. package/dist/sandbox/helpers.cjs +129 -0
  20. package/dist/sandbox/helpers.d.ts +29 -0
  21. package/dist/sandbox/helpers.js +123 -0
  22. package/dist/sandbox/index.cjs +7 -1
  23. package/dist/sandbox/index.d.ts +5 -1
  24. package/dist/sandbox/index.js +2 -0
  25. package/dist/sandbox/mounts.cjs +21 -3
  26. package/dist/sandbox/mounts.d.ts +7 -1
  27. package/dist/sandbox/mounts.js +21 -3
  28. package/dist/sandbox/proxy_config.cjs +41 -10
  29. package/dist/sandbox/proxy_config.d.ts +33 -6
  30. package/dist/sandbox/proxy_config.js +40 -10
  31. package/dist/sandbox/sandbox.cjs +160 -4
  32. package/dist/sandbox/sandbox.d.ts +71 -1
  33. package/dist/sandbox/sandbox.js +161 -5
  34. package/dist/sandbox/service_url.cjs +125 -0
  35. package/dist/sandbox/service_url.d.ts +50 -0
  36. package/dist/sandbox/service_url.js +120 -0
  37. package/dist/sandbox/types.d.ts +251 -8
  38. package/dist/sandbox/ws_execute.cjs +31 -1
  39. package/dist/sandbox/ws_execute.d.ts +9 -0
  40. package/dist/sandbox/ws_execute.js +31 -1
  41. package/dist/utils/error.cjs +17 -22
  42. package/dist/utils/error.js +17 -22
  43. package/dist/wrappers/openai_agents.cjs +37 -28
  44. package/dist/wrappers/openai_agents.d.ts +0 -1
  45. package/dist/wrappers/openai_agents.js +37 -28
  46. package/package.json +2 -2
@@ -258,3 +258,126 @@ export async function handleSandboxHttpError(response) {
258
258
  }
259
259
  throw new LangSmithSandboxError(message);
260
260
  }
261
+ // =============================================================================
262
+ // Run configuration
263
+ // =============================================================================
264
+ /**
265
+ * Reject a per-command `runConfig` alongside the deprecated `env`/`cwd`.
266
+ *
267
+ * The server answers 400 for a request carrying both spellings rather than
268
+ * letting one silently win, so refuse it here where the message can name the
269
+ * replacement.
270
+ */
271
+ export function assertRunConfigNotCombined(options) {
272
+ if (options.runConfig !== undefined &&
273
+ (options.env !== undefined || options.cwd !== undefined)) {
274
+ throw new LangSmithValidationError("Cannot combine runConfig with the deprecated env/cwd options. " +
275
+ "Use runConfig.env_vars and runConfig.work_dir instead.");
276
+ }
277
+ }
278
+ /**
279
+ * Whether to half-close stdin at spawn.
280
+ *
281
+ * Defaults on for a non-PTY command: a command that reads stdin otherwise
282
+ * blocks on a pipe nobody writes to until the timeout kills it. A PTY has no
283
+ * separate write end to close, so the server ignores the flag there.
284
+ */
285
+ export function resolveCloseInput(closeInput, pty) {
286
+ if (pty)
287
+ return false;
288
+ return closeInput ?? true;
289
+ }
290
+ // =============================================================================
291
+ // Ranged downloads
292
+ // =============================================================================
293
+ /** Render a byte range as an RFC 9110 `Range` header value. */
294
+ export function buildRangeHeader(options) {
295
+ const { start, end, suffixBytes } = options;
296
+ if (suffixBytes !== undefined) {
297
+ if (start !== undefined || end !== undefined) {
298
+ throw new LangSmithValidationError("Cannot combine suffixBytes with start/end.");
299
+ }
300
+ if (suffixBytes <= 0) {
301
+ throw new LangSmithValidationError("suffixBytes must be positive.");
302
+ }
303
+ return `bytes=-${suffixBytes}`;
304
+ }
305
+ if (start === undefined) {
306
+ throw new LangSmithValidationError("Provide start (with optional end), or suffixBytes.");
307
+ }
308
+ if (start < 0) {
309
+ throw new LangSmithValidationError("start must not be negative.");
310
+ }
311
+ if (end === undefined) {
312
+ return `bytes=${start}-`;
313
+ }
314
+ if (end < start) {
315
+ throw new LangSmithValidationError("end must not precede start.");
316
+ }
317
+ return `bytes=${start}-${end}`;
318
+ }
319
+ /** Read the first byte offset and total size out of `Content-Range`. */
320
+ function parseContentRange(value) {
321
+ if (!value || !value.startsWith("bytes "))
322
+ return { start: 0 };
323
+ const spec = value.slice("bytes ".length).trim();
324
+ const [rangePart, totalPart] = spec.split("/");
325
+ const first = rangePart.split("-")[0].trim();
326
+ const start = /^\d+$/.test(first) ? Number(first) : 0;
327
+ const total = /^\d+$/.test((totalPart ?? "").trim())
328
+ ? Number(totalPart)
329
+ : undefined;
330
+ return { start, total };
331
+ }
332
+ /** Build a FileStat from a HEAD response's headers. */
333
+ export function fileStatFromResponse(response) {
334
+ const length = response.headers.get("content-length");
335
+ return {
336
+ size_bytes: length && /^\d+$/.test(length) ? Number(length) : 0,
337
+ etag: response.headers.get("etag") ?? undefined,
338
+ last_modified: response.headers.get("last-modified") ?? undefined,
339
+ content_type: response.headers.get("content-type") ?? undefined,
340
+ };
341
+ }
342
+ /** Build a FileChunk from a ranged download response. */
343
+ export async function fileChunkFromResponse(response) {
344
+ const etag = response.headers.get("etag") ?? undefined;
345
+ const lastModified = response.headers.get("last-modified") ?? undefined;
346
+ if (response.status === 304) {
347
+ return {
348
+ content: new Uint8Array(0),
349
+ etag,
350
+ start: 0,
351
+ partial: false,
352
+ unchanged: true,
353
+ last_modified: lastModified,
354
+ };
355
+ }
356
+ const content = new Uint8Array(await response.arrayBuffer());
357
+ const partial = response.status === 206;
358
+ // A stale If-Range answers 200 with the whole file; the caller has to
359
+ // restart rather than append, so report it from byte zero.
360
+ const { start, total } = partial
361
+ ? parseContentRange(response.headers.get("content-range"))
362
+ : { start: 0, total: content.length };
363
+ return {
364
+ content,
365
+ etag,
366
+ total_bytes: total,
367
+ start,
368
+ partial,
369
+ unchanged: false,
370
+ last_modified: lastModified,
371
+ };
372
+ }
373
+ /** Map a file-operation HTTP error, including the non-JSON 416. */
374
+ export async function handleFileHttpError(response, path, sandboxName) {
375
+ if (response.status === 404) {
376
+ throw new LangSmithResourceNotFoundError(`File '${path}' not found in sandbox '${sandboxName}'`, "file");
377
+ }
378
+ if (response.status === 416) {
379
+ throw new LangSmithSandboxOperationError(`Requested range for '${path}' starts past the end of the file ` +
380
+ `(${response.headers.get("content-range") ?? "unknown size"}).`, "read");
381
+ }
382
+ return handleSandboxHttpError(response);
383
+ }
@@ -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.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;
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.SERVICE_TOKEN_HEADER = exports.ServiceLoginUrl = exports.ServiceUrl = exports.validateAccessDelegation = 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; } });
@@ -43,6 +43,12 @@ Object.defineProperty(exports, "gcpAuth", { enumerable: true, get: function () {
43
43
  Object.defineProperty(exports, "opaqueSecret", { enumerable: true, get: function () { return proxy_config_js_1.opaqueSecret; } });
44
44
  Object.defineProperty(exports, "proxyConfig", { enumerable: true, get: function () { return proxy_config_js_1.proxyConfig; } });
45
45
  Object.defineProperty(exports, "workspaceSecret", { enumerable: true, get: function () { return proxy_config_js_1.workspaceSecret; } });
46
+ var access_delegation_js_1 = require("./access_delegation.cjs");
47
+ Object.defineProperty(exports, "validateAccessDelegation", { enumerable: true, get: function () { return access_delegation_js_1.validateAccessDelegation; } });
48
+ var service_url_js_1 = require("./service_url.cjs");
49
+ Object.defineProperty(exports, "ServiceUrl", { enumerable: true, get: function () { return service_url_js_1.ServiceUrl; } });
50
+ Object.defineProperty(exports, "ServiceLoginUrl", { enumerable: true, get: function () { return service_url_js_1.ServiceLoginUrl; } });
51
+ Object.defineProperty(exports, "SERVICE_TOKEN_HEADER", { enumerable: true, get: function () { return service_url_js_1.SERVICE_TOKEN_HEADER; } });
46
52
  var mounts_js_1 = require("./mounts.cjs");
47
53
  Object.defineProperty(exports, "contextHubMount", { enumerable: true, get: function () { return mounts_js_1.contextHubMount; } });
48
54
  Object.defineProperty(exports, "gcsMount", { enumerable: true, get: function () { return mounts_js_1.gcsMount; } });
@@ -31,6 +31,10 @@ export { SandboxClient } from "./client.js";
31
31
  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
+ export { validateAccessDelegation } from "./access_delegation.js";
35
+ export { ServiceUrl, ServiceLoginUrl, SERVICE_TOKEN_HEADER, } from "./service_url.js";
34
36
  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, DownloadContentDisposition, DownloadURL, GenerateDownloadURLOptions, ListSnapshotsOptions, WaitForSnapshotOptions, StartSandboxOptions, UpdateSandboxOptions, WaitForSandboxOptions, } from "./types.js";
37
+ export type { AccessDelegation, AccessDelegationMode, } from "./access_delegation.js";
38
+ export type { ServiceAccess, ServiceUrlData } from "./service_url.js";
39
+ export type { ExecutionResult, FileChunk, FileInfo, FileStat, GlobOptions, GlobResult, GrepMatch, GrepOptions, GrepResult, ReadRangeOptions, RunConfig, 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
40
  export { LangSmithSandboxError, LangSmithSandboxAPIError, LangSmithSandboxAuthenticationError, LangSmithSandboxConnectionError, LangSmithSandboxRetryableConnectionError, LangSmithSandboxConnectTimeoutError, LangSmithSandboxServerReloadError, LangSmithResourceNotFoundError, LangSmithResourceTimeoutError, LangSmithResourceInUseError, LangSmithResourceAlreadyExistsError, LangSmithResourceNameConflictError, LangSmithValidationError, LangSmithQuotaExceededError, LangSmithResourceCreationError, LangSmithSandboxCreationError, LangSmithSandboxNotReadyError, LangSmithSandboxOperationError, LangSmithCommandTimeoutError, LangSmithDataplaneNotConfiguredError, } from "./errors.js";
@@ -32,6 +32,8 @@ export { SandboxClient } from "./client.js";
32
32
  export { Sandbox } from "./sandbox.js";
33
33
  export { CommandHandle } from "./command_handle.js";
34
34
  export { awsAuth, gcpAuth, opaqueSecret, proxyConfig, workspaceSecret, } from "./proxy_config.js";
35
+ export { validateAccessDelegation } from "./access_delegation.js";
36
+ export { ServiceUrl, ServiceLoginUrl, SERVICE_TOKEN_HEADER, } from "./service_url.js";
35
37
  export { contextHubMount, gcsMount, gitMount, mountConfig, s3Mount, } from "./mounts.js";
36
38
  // Errors
37
39
  export {
@@ -6,6 +6,7 @@ exports.gcsMount = gcsMount;
6
6
  exports.contextHubMount = contextHubMount;
7
7
  exports.mountConfig = mountConfig;
8
8
  exports.validateMountConfigProxyConfig = validateMountConfigProxyConfig;
9
+ const proxy_config_js_1 = require("./proxy_config.cjs");
9
10
  function requireNonEmptyString(value, field) {
10
11
  if (typeof value !== "string" || value.trim() === "") {
11
12
  throw new Error(`${field} must be a non-empty string`);
@@ -217,6 +218,11 @@ function normalizeMountAuth(auth) {
217
218
  if (aws === undefined) {
218
219
  throw new Error("aws mount auth must include an aws block");
219
220
  }
221
+ const roleArn = (0, proxy_config_js_1.getAwsRoleArn)(aws);
222
+ if (roleArn !== undefined) {
223
+ byProvider.aws = { role_arn: roleArn };
224
+ continue;
225
+ }
220
226
  byProvider.aws = {
221
227
  access_key_id: copyMountSecret(aws.access_key_id, "accessKeyId"),
222
228
  secret_access_key: copyMountSecret(aws.secret_access_key, "secretAccessKey"),
@@ -234,11 +240,21 @@ function normalizeMountAuth(auth) {
234
240
  }
235
241
  return byProvider;
236
242
  }
237
- function mountConfig({ auth = [], mounts, }) {
243
+ /**
244
+ * Build mount-scoped auth, or use an enabled AWS rule from proxyConfig for S3.
245
+ * Pass the same proxyConfig to sandbox creation; its general IAM permissions
246
+ * remain unchanged. GCS mounts still require explicit GCP auth in this config.
247
+ */
248
+ function mountConfig({ auth = [], mounts, proxyConfig, }) {
238
249
  const normalizedMounts = normalizeMounts(mounts);
239
250
  const authByProvider = normalizeMountAuth(auth);
240
251
  const mountProviders = new Set(normalizedMounts.map((mount) => mount.type));
241
- if (mountProviders.has("s3") && authByProvider.aws === undefined) {
252
+ const proxyProviders = new Set((proxyConfig?.rules ?? [])
253
+ .filter((rule) => rule?.enabled === true)
254
+ .map((rule) => rule.type));
255
+ if (mountProviders.has("s3") &&
256
+ authByProvider.aws === undefined &&
257
+ !proxyProviders.has("aws")) {
242
258
  throw new Error("s3 mounts require aws auth in mountConfig");
243
259
  }
244
260
  if (mountProviders.has("gcs") && authByProvider.gcp === undefined) {
@@ -250,10 +266,12 @@ function mountConfig({ auth = [], mounts, }) {
250
266
  if (authByProvider.gcp !== undefined && !mountProviders.has("gcs")) {
251
267
  throw new Error("gcp auth requires at least one gcs mount in mountConfig");
252
268
  }
253
- return {
269
+ const config = {
254
270
  auth: authByProvider,
255
271
  mounts: normalizedMounts,
256
272
  };
273
+ validateMountConfigProxyConfig(config, proxyConfig);
274
+ return config;
257
275
  }
258
276
  function validateMountConfigProxyConfig(mountConfig, proxyConfig) {
259
277
  if (proxyConfig === undefined) {
@@ -36,8 +36,14 @@ export declare function contextHubMount({ id, mountPath, repo, initialPullOnly,
36
36
  repo: string;
37
37
  initialPullOnly?: boolean;
38
38
  }): ContextHubMountSpec;
39
- export declare function mountConfig({ auth, mounts, }: {
39
+ /**
40
+ * Build mount-scoped auth, or use an enabled AWS rule from proxyConfig for S3.
41
+ * Pass the same proxyConfig to sandbox creation; its general IAM permissions
42
+ * remain unchanged. GCS mounts still require explicit GCP auth in this config.
43
+ */
44
+ export declare function mountConfig({ auth, mounts, proxyConfig, }: {
40
45
  auth?: SandboxMountAuth[];
41
46
  mounts: SandboxMount[];
47
+ proxyConfig?: SandboxProxyConfig;
42
48
  }): SandboxMountConfig;
43
49
  export declare function validateMountConfigProxyConfig(mountConfig: SandboxMountConfig, proxyConfig: SandboxProxyConfig | undefined): void;
@@ -1,3 +1,4 @@
1
+ import { getAwsRoleArn } from "./proxy_config.js";
1
2
  function requireNonEmptyString(value, field) {
2
3
  if (typeof value !== "string" || value.trim() === "") {
3
4
  throw new Error(`${field} must be a non-empty string`);
@@ -209,6 +210,11 @@ function normalizeMountAuth(auth) {
209
210
  if (aws === undefined) {
210
211
  throw new Error("aws mount auth must include an aws block");
211
212
  }
213
+ const roleArn = getAwsRoleArn(aws);
214
+ if (roleArn !== undefined) {
215
+ byProvider.aws = { role_arn: roleArn };
216
+ continue;
217
+ }
212
218
  byProvider.aws = {
213
219
  access_key_id: copyMountSecret(aws.access_key_id, "accessKeyId"),
214
220
  secret_access_key: copyMountSecret(aws.secret_access_key, "secretAccessKey"),
@@ -226,11 +232,21 @@ function normalizeMountAuth(auth) {
226
232
  }
227
233
  return byProvider;
228
234
  }
229
- export function mountConfig({ auth = [], mounts, }) {
235
+ /**
236
+ * Build mount-scoped auth, or use an enabled AWS rule from proxyConfig for S3.
237
+ * Pass the same proxyConfig to sandbox creation; its general IAM permissions
238
+ * remain unchanged. GCS mounts still require explicit GCP auth in this config.
239
+ */
240
+ export function mountConfig({ auth = [], mounts, proxyConfig, }) {
230
241
  const normalizedMounts = normalizeMounts(mounts);
231
242
  const authByProvider = normalizeMountAuth(auth);
232
243
  const mountProviders = new Set(normalizedMounts.map((mount) => mount.type));
233
- if (mountProviders.has("s3") && authByProvider.aws === undefined) {
244
+ const proxyProviders = new Set((proxyConfig?.rules ?? [])
245
+ .filter((rule) => rule?.enabled === true)
246
+ .map((rule) => rule.type));
247
+ if (mountProviders.has("s3") &&
248
+ authByProvider.aws === undefined &&
249
+ !proxyProviders.has("aws")) {
234
250
  throw new Error("s3 mounts require aws auth in mountConfig");
235
251
  }
236
252
  if (mountProviders.has("gcs") && authByProvider.gcp === undefined) {
@@ -242,10 +258,12 @@ export function mountConfig({ auth = [], mounts, }) {
242
258
  if (authByProvider.gcp !== undefined && !mountProviders.has("gcs")) {
243
259
  throw new Error("gcp auth requires at least one gcs mount in mountConfig");
244
260
  }
245
- return {
261
+ const config = {
246
262
  auth: authByProvider,
247
263
  mounts: normalizedMounts,
248
264
  };
265
+ validateMountConfigProxyConfig(config, proxyConfig);
266
+ return config;
249
267
  }
250
268
  export function validateMountConfigProxyConfig(mountConfig, proxyConfig) {
251
269
  if (proxyConfig === undefined) {
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAwsRoleArn = getAwsRoleArn;
3
4
  exports.workspaceSecret = workspaceSecret;
4
5
  exports.opaqueSecret = opaqueSecret;
5
6
  exports.proxyConfig = proxyConfig;
@@ -49,6 +50,10 @@ function requireProxyRules(rules) {
49
50
  });
50
51
  }
51
52
  function validateProxyProviderRule(rule) {
53
+ if (rule.type === "aws") {
54
+ getAwsRoleArn(rule.aws);
55
+ return;
56
+ }
52
57
  if (rule.type !== "gcp") {
53
58
  return;
54
59
  }
@@ -58,6 +63,21 @@ function validateProxyProviderRule(rule) {
58
63
  }
59
64
  requireNonEmptyStringArray(gcp.scopes, "scopes");
60
65
  }
66
+ /** Validate role-only descriptors without tightening legacy static inputs. */
67
+ function getAwsRoleArn(aws) {
68
+ if (aws === null || typeof aws !== "object" || Array.isArray(aws)) {
69
+ return undefined;
70
+ }
71
+ const config = aws;
72
+ if (!("role_arn" in config) || config.role_arn === "") {
73
+ return undefined;
74
+ }
75
+ const roleArn = requireNonEmptyString(config.role_arn, "roleArn");
76
+ if (Object.keys(config).some((key) => key !== "role_arn")) {
77
+ throw new Error("AWS role auth must contain only role_arn");
78
+ }
79
+ return roleArn;
80
+ }
61
81
  /** Reference a LangSmith workspace secret in a sandbox proxy configuration. */
62
82
  function workspaceSecret(name) {
63
83
  const normalized = requireNonEmptyString(name, "name");
@@ -82,28 +102,39 @@ function opaqueSecret(value) {
82
102
  };
83
103
  }
84
104
  /** Build a sandbox proxy config from one or more proxy rules. */
85
- function proxyConfig({ rules, noProxy, accessControl, } = {}) {
105
+ function proxyConfig({ rules, accessControl, } = {}) {
86
106
  const config = {
87
107
  rules: requireProxyRules(rules),
88
108
  };
89
- if (noProxy !== undefined) {
90
- config.no_proxy = requireNonEmptyStringArray(noProxy, "noProxy");
91
- }
92
109
  if (accessControl !== undefined) {
93
110
  config.access_control = { ...accessControl };
94
111
  }
95
112
  return config;
96
113
  }
97
- /** Build a sandbox proxy rule that signs AWS HTTPS requests with SigV4. */
98
- function awsAuth({ accessKeyId, secretAccessKey, name = "aws", enabled = true, envVars, }) {
114
+ function awsAuth({ accessKeyId, secretAccessKey, roleArn, name = "aws", enabled = true, envVars, }) {
115
+ const candidate = {};
116
+ if (roleArn !== undefined)
117
+ candidate.role_arn = roleArn;
118
+ if (accessKeyId !== undefined)
119
+ candidate.access_key_id = accessKeyId;
120
+ if (secretAccessKey !== undefined)
121
+ candidate.secret_access_key = secretAccessKey;
122
+ const normalizedRole = getAwsRoleArn(candidate);
123
+ let aws;
124
+ if (normalizedRole !== undefined) {
125
+ aws = { role_arn: normalizedRole };
126
+ }
127
+ else {
128
+ if (accessKeyId === undefined || secretAccessKey === undefined) {
129
+ throw new Error("AWS auth requires roleArn or both static credentials");
130
+ }
131
+ aws = { access_key_id: accessKeyId, secret_access_key: secretAccessKey };
132
+ }
99
133
  const rule = {
100
134
  name: requireNonEmptyString(name, "name"),
101
135
  type: "aws",
102
136
  enabled,
103
- aws: {
104
- access_key_id: accessKeyId,
105
- secret_access_key: secretAccessKey,
106
- },
137
+ aws,
107
138
  };
108
139
  if (envVars !== undefined) {
109
140
  rule.env_vars = requireEnvVars(envVars);
@@ -1,22 +1,48 @@
1
1
  import type { SandboxAccessControl, SandboxAwsAuthRule, SandboxGcpAuthRule, SandboxProxyConfig, SandboxProxyRule, SandboxProxySecret } from "./types.js";
2
+ /** Validate role-only descriptors without tightening legacy static inputs. */
3
+ export declare function getAwsRoleArn(aws: unknown): string | undefined;
2
4
  /** Reference a LangSmith workspace secret in a sandbox proxy configuration. */
3
5
  export declare function workspaceSecret(name: string): SandboxProxySecret;
4
6
  /** Provide a write-only secret value for a sandbox proxy configuration. */
5
7
  export declare function opaqueSecret(value: string): SandboxProxySecret;
6
8
  /** Build a sandbox proxy config from one or more proxy rules. */
7
- export declare function proxyConfig({ rules, noProxy, accessControl, }?: {
9
+ export declare function proxyConfig({ rules, accessControl, }?: {
8
10
  rules?: SandboxProxyRule[];
11
+ /** @deprecated Ignored. The sandbox runtime has no proxy bypass list. */
9
12
  noProxy?: string[];
10
13
  accessControl?: SandboxAccessControl;
11
14
  }): SandboxProxyConfig;
12
- /** Build a sandbox proxy rule that signs AWS HTTPS requests with SigV4. */
13
- export declare function awsAuth({ accessKeyId, secretAccessKey, name, enabled, envVars, }: {
14
- accessKeyId: SandboxProxySecret;
15
- secretAccessKey: SandboxProxySecret;
15
+ interface AwsAuthCommonOptions {
16
16
  name?: string;
17
17
  enabled?: boolean;
18
18
  envVars?: Record<string, string>;
19
- }): SandboxAwsAuthRule;
19
+ }
20
+ type AwsStaticAuthConfig = Extract<SandboxAwsAuthRule["aws"], {
21
+ access_key_id: SandboxProxySecret;
22
+ }>;
23
+ type AwsRoleAuthConfig = Extract<SandboxAwsAuthRule["aws"], {
24
+ role_arn: string;
25
+ }>;
26
+ type AwsStaticAuthOptions = AwsAuthCommonOptions & {
27
+ accessKeyId: SandboxProxySecret;
28
+ secretAccessKey: SandboxProxySecret;
29
+ roleArn?: "";
30
+ };
31
+ type AwsRoleAuthOptions = AwsAuthCommonOptions & {
32
+ roleArn: string;
33
+ accessKeyId?: never;
34
+ secretAccessKey?: never;
35
+ };
36
+ /**
37
+ * Sign supported AWS HTTPS requests using static keys or an IAM role.
38
+ * Role auth requires backend support and is configured at sandbox creation.
39
+ * LangSmith supplies the workspace External ID and renews credentials.
40
+ * A role in proxyConfig uses its effective IAM permissions; the same helper
41
+ * in mountConfig.auth uses the backend's mount-scoped S3 permissions.
42
+ */
43
+ export declare function awsAuth(options: AwsStaticAuthOptions): SandboxAwsAuthRule<AwsStaticAuthConfig>;
44
+ export declare function awsAuth(options: AwsRoleAuthOptions): SandboxAwsAuthRule<AwsRoleAuthConfig>;
45
+ export declare function awsAuth(options: AwsStaticAuthOptions | AwsRoleAuthOptions): SandboxAwsAuthRule;
20
46
  /** Build a sandbox proxy rule that injects GCP OAuth bearer auth. */
21
47
  export declare function gcpAuth({ serviceAccountJson, scopes, name, enabled, envVars, }: {
22
48
  serviceAccountJson: SandboxProxySecret;
@@ -25,3 +51,4 @@ export declare function gcpAuth({ serviceAccountJson, scopes, name, enabled, env
25
51
  enabled?: boolean;
26
52
  envVars?: Record<string, string>;
27
53
  }): SandboxGcpAuthRule;
54
+ export {};
@@ -42,6 +42,10 @@ function requireProxyRules(rules) {
42
42
  });
43
43
  }
44
44
  function validateProxyProviderRule(rule) {
45
+ if (rule.type === "aws") {
46
+ getAwsRoleArn(rule.aws);
47
+ return;
48
+ }
45
49
  if (rule.type !== "gcp") {
46
50
  return;
47
51
  }
@@ -51,6 +55,21 @@ function validateProxyProviderRule(rule) {
51
55
  }
52
56
  requireNonEmptyStringArray(gcp.scopes, "scopes");
53
57
  }
58
+ /** Validate role-only descriptors without tightening legacy static inputs. */
59
+ export function getAwsRoleArn(aws) {
60
+ if (aws === null || typeof aws !== "object" || Array.isArray(aws)) {
61
+ return undefined;
62
+ }
63
+ const config = aws;
64
+ if (!("role_arn" in config) || config.role_arn === "") {
65
+ return undefined;
66
+ }
67
+ const roleArn = requireNonEmptyString(config.role_arn, "roleArn");
68
+ if (Object.keys(config).some((key) => key !== "role_arn")) {
69
+ throw new Error("AWS role auth must contain only role_arn");
70
+ }
71
+ return roleArn;
72
+ }
54
73
  /** Reference a LangSmith workspace secret in a sandbox proxy configuration. */
55
74
  export function workspaceSecret(name) {
56
75
  const normalized = requireNonEmptyString(name, "name");
@@ -75,28 +94,39 @@ export function opaqueSecret(value) {
75
94
  };
76
95
  }
77
96
  /** Build a sandbox proxy config from one or more proxy rules. */
78
- export function proxyConfig({ rules, noProxy, accessControl, } = {}) {
97
+ export function proxyConfig({ rules, accessControl, } = {}) {
79
98
  const config = {
80
99
  rules: requireProxyRules(rules),
81
100
  };
82
- if (noProxy !== undefined) {
83
- config.no_proxy = requireNonEmptyStringArray(noProxy, "noProxy");
84
- }
85
101
  if (accessControl !== undefined) {
86
102
  config.access_control = { ...accessControl };
87
103
  }
88
104
  return config;
89
105
  }
90
- /** Build a sandbox proxy rule that signs AWS HTTPS requests with SigV4. */
91
- export function awsAuth({ accessKeyId, secretAccessKey, name = "aws", enabled = true, envVars, }) {
106
+ export function awsAuth({ accessKeyId, secretAccessKey, roleArn, name = "aws", enabled = true, envVars, }) {
107
+ const candidate = {};
108
+ if (roleArn !== undefined)
109
+ candidate.role_arn = roleArn;
110
+ if (accessKeyId !== undefined)
111
+ candidate.access_key_id = accessKeyId;
112
+ if (secretAccessKey !== undefined)
113
+ candidate.secret_access_key = secretAccessKey;
114
+ const normalizedRole = getAwsRoleArn(candidate);
115
+ let aws;
116
+ if (normalizedRole !== undefined) {
117
+ aws = { role_arn: normalizedRole };
118
+ }
119
+ else {
120
+ if (accessKeyId === undefined || secretAccessKey === undefined) {
121
+ throw new Error("AWS auth requires roleArn or both static credentials");
122
+ }
123
+ aws = { access_key_id: accessKeyId, secret_access_key: secretAccessKey };
124
+ }
92
125
  const rule = {
93
126
  name: requireNonEmptyString(name, "name"),
94
127
  type: "aws",
95
128
  enabled,
96
- aws: {
97
- access_key_id: accessKeyId,
98
- secret_access_key: secretAccessKey,
99
- },
129
+ aws,
100
130
  };
101
131
  if (envVars !== undefined) {
102
132
  rule.env_vars = requireEnvVars(envVars);