langsmith 0.8.7 → 0.8.9

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 (83) hide show
  1. package/dist/_openapi_client/client.cjs +8 -0
  2. package/dist/_openapi_client/client.d.ts +3 -0
  3. package/dist/_openapi_client/client.js +8 -0
  4. package/dist/_openapi_client/resources/annotation-queues/annotation-queues.cjs +157 -0
  5. package/dist/_openapi_client/resources/annotation-queues/annotation-queues.d.ts +382 -0
  6. package/dist/_openapi_client/resources/annotation-queues/annotation-queues.js +120 -0
  7. package/dist/_openapi_client/resources/annotation-queues/items.cjs +84 -0
  8. package/dist/_openapi_client/resources/annotation-queues/items.d.ts +207 -0
  9. package/dist/_openapi_client/resources/annotation-queues/items.js +80 -0
  10. package/dist/_openapi_client/resources/annotation-queues/runs.cjs +73 -0
  11. package/dist/_openapi_client/resources/annotation-queues/runs.d.ts +197 -0
  12. package/dist/_openapi_client/resources/annotation-queues/runs.js +69 -0
  13. package/dist/_openapi_client/resources/datasets/experiment-runs.cjs +3 -1
  14. package/dist/_openapi_client/resources/datasets/experiment-runs.d.ts +2 -0
  15. package/dist/_openapi_client/resources/datasets/experiment-runs.js +3 -1
  16. package/dist/_openapi_client/resources/index.cjs +3 -1
  17. package/dist/_openapi_client/resources/index.d.ts +1 -0
  18. package/dist/_openapi_client/resources/index.js +1 -0
  19. package/dist/_openapi_client/resources/info.cjs +3 -1
  20. package/dist/_openapi_client/resources/info.d.ts +16 -22
  21. package/dist/_openapi_client/resources/info.js +3 -1
  22. package/dist/_openapi_client/resources/issues.cjs +2 -2
  23. package/dist/_openapi_client/resources/issues.d.ts +1 -1
  24. package/dist/_openapi_client/resources/issues.js +2 -2
  25. package/dist/_openapi_client/resources/online-evaluators.cjs +7 -7
  26. package/dist/_openapi_client/resources/online-evaluators.d.ts +8 -0
  27. package/dist/_openapi_client/resources/online-evaluators.js +7 -7
  28. package/dist/_openapi_client/resources/public/runs.cjs +10 -8
  29. package/dist/_openapi_client/resources/public/runs.d.ts +9 -7
  30. package/dist/_openapi_client/resources/public/runs.js +10 -8
  31. package/dist/_openapi_client/resources/runs/runs.cjs +14 -9
  32. package/dist/_openapi_client/resources/runs/runs.d.ts +20 -10
  33. package/dist/_openapi_client/resources/runs/runs.js +14 -9
  34. package/dist/_openapi_client/resources/runs/share.cjs +6 -2
  35. package/dist/_openapi_client/resources/runs/share.d.ts +4 -0
  36. package/dist/_openapi_client/resources/runs/share.js +6 -2
  37. package/dist/_openapi_client/resources/sandboxes/boxes.cjs +10 -10
  38. package/dist/_openapi_client/resources/sandboxes/boxes.d.ts +40 -0
  39. package/dist/_openapi_client/resources/sandboxes/boxes.js +10 -10
  40. package/dist/_openapi_client/resources/sandboxes/registries.cjs +5 -5
  41. package/dist/_openapi_client/resources/sandboxes/registries.js +5 -5
  42. package/dist/_openapi_client/resources/sandboxes/sandboxes.d.ts +17 -0
  43. package/dist/_openapi_client/resources/sandboxes/snapshots.cjs +4 -4
  44. package/dist/_openapi_client/resources/sandboxes/snapshots.d.ts +12 -0
  45. package/dist/_openapi_client/resources/sandboxes/snapshots.js +4 -4
  46. package/dist/_openapi_client/resources/threads.cjs +14 -11
  47. package/dist/_openapi_client/resources/threads.d.ts +13 -9
  48. package/dist/_openapi_client/resources/threads.js +14 -11
  49. package/dist/_openapi_client/resources/traces.cjs +8 -5
  50. package/dist/_openapi_client/resources/traces.d.ts +7 -4
  51. package/dist/_openapi_client/resources/traces.js +8 -5
  52. package/dist/client.cjs +318 -40
  53. package/dist/client.d.ts +111 -25
  54. package/dist/client.js +318 -40
  55. package/dist/evaluation/evaluate_comparative.cjs +7 -4
  56. package/dist/evaluation/evaluate_comparative.js +7 -4
  57. package/dist/index.cjs +1 -1
  58. package/dist/index.d.ts +1 -1
  59. package/dist/index.js +1 -1
  60. package/dist/sandbox/command_handle.cjs +38 -11
  61. package/dist/sandbox/command_handle.d.ts +1 -0
  62. package/dist/sandbox/command_handle.js +39 -12
  63. package/dist/sandbox/errors.cjs +16 -1
  64. package/dist/sandbox/errors.d.ts +11 -0
  65. package/dist/sandbox/errors.js +14 -0
  66. package/dist/sandbox/index.cjs +2 -1
  67. package/dist/sandbox/index.d.ts +1 -1
  68. package/dist/sandbox/index.js +1 -1
  69. package/dist/sandbox/sandbox.cjs +22 -4
  70. package/dist/sandbox/sandbox.js +24 -6
  71. package/dist/sandbox/ws_execute.cjs +82 -11
  72. package/dist/sandbox/ws_execute.d.ts +12 -2
  73. package/dist/sandbox/ws_execute.js +79 -11
  74. package/dist/utils/v2_migration.cjs +18 -0
  75. package/dist/utils/v2_migration.d.ts +8 -0
  76. package/dist/utils/v2_migration.js +16 -0
  77. package/dist/utils/warn.cjs +30 -4
  78. package/dist/utils/warn.d.ts +4 -1
  79. package/dist/utils/warn.js +29 -4
  80. package/package.json +1 -1
  81. package/dist/utils/constants.cjs +0 -4
  82. package/dist/utils/constants.d.ts +0 -1
  83. package/dist/utils/constants.js +0 -1
package/dist/index.cjs CHANGED
@@ -35,6 +35,6 @@ Object.defineProperty(exports, "UnprocessableEntityError", { enumerable: true, g
35
35
  Object.defineProperty(exports, "RateLimitError", { enumerable: true, get: function () { return error_js_1.RateLimitError; } });
36
36
  Object.defineProperty(exports, "InternalServerError", { enumerable: true, get: function () { return error_js_1.InternalServerError; } });
37
37
  // Update using pnpm bump-version
38
- exports.__version__ = "0.8.7";
38
+ exports.__version__ = "0.8.9";
39
39
  // Metadata key to hide a traced run from LangSmith's Messages View.
40
40
  exports.LS_MESSAGE_VIEW_EXCLUDE = "ls_message_view_exclude";
package/dist/index.d.ts CHANGED
@@ -7,5 +7,5 @@ export { computeRunIdForSecondaryReplica, uuid7, uuid7FromTime, } from "./uuid.j
7
7
  export { isTracingEnabled } from "./utils/guard.js";
8
8
  export { Cache, PromptCache, type CacheConfig, type CacheMetrics, configureGlobalPromptCache, promptCacheSingleton, } from "./utils/prompt_cache/index.js";
9
9
  export { LangsmithError, APIError, APIUserAbortError, APIConnectionError, APIConnectionTimeoutError, BadRequestError, AuthenticationError, PermissionDeniedError, NotFoundError, ConflictError, UnprocessableEntityError, RateLimitError, InternalServerError, } from "./_openapi_client/core/error.js";
10
- export declare const __version__ = "0.8.7";
10
+ export declare const __version__ = "0.8.9";
11
11
  export declare const LS_MESSAGE_VIEW_EXCLUDE: "ls_message_view_exclude";
package/dist/index.js CHANGED
@@ -7,6 +7,6 @@ export { isTracingEnabled } from "./utils/guard.js";
7
7
  export { Cache, PromptCache, configureGlobalPromptCache, promptCacheSingleton, } from "./utils/prompt_cache/index.js";
8
8
  export { LangsmithError, APIError, APIUserAbortError, APIConnectionError, APIConnectionTimeoutError, BadRequestError, AuthenticationError, PermissionDeniedError, NotFoundError, ConflictError, UnprocessableEntityError, RateLimitError, InternalServerError, } from "./_openapi_client/core/error.js";
9
9
  // Update using pnpm bump-version
10
- export const __version__ = "0.8.7";
10
+ export const __version__ = "0.8.9";
11
11
  // Metadata key to hide a traced run from LangSmith's Messages View.
12
12
  export const LS_MESSAGE_VIEW_EXCLUDE = "ls_message_view_exclude";
@@ -8,6 +8,22 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.CommandHandle = void 0;
10
10
  const errors_js_1 = require("./errors.cjs");
11
+ /**
12
+ * Whether a 'started' frame proves *this* command was reattached to.
13
+ *
14
+ * A command and its WebSocket are separate things: the command keeps running on
15
+ * the server and the socket is only this client's attachment to it. The server
16
+ * acknowledges every successful reattachment with 'started', which for a command
17
+ * that produces no output is the only evidence it landed. One naming a different
18
+ * command is not evidence, and must not clear the reconnect budget.
19
+ */
20
+ function acknowledgesReconnect(msg, commandId) {
21
+ const acked = msg.command_id;
22
+ if (acked === commandId)
23
+ return true;
24
+ console.warn(`Ignoring reconnect acknowledgement for command ${String(acked)} while attached to ${String(commandId)}`);
25
+ return false;
26
+ }
11
27
  /**
12
28
  * Async handle to a running command with streaming output and auto-reconnect.
13
29
  *
@@ -107,6 +123,12 @@ class CommandHandle {
107
123
  writable: true,
108
124
  value: void 0
109
125
  });
126
+ Object.defineProperty(this, "_reconnectAttempts", {
127
+ enumerable: true,
128
+ configurable: true,
129
+ writable: true,
130
+ value: 0
131
+ });
110
132
  Object.defineProperty(this, "_onStdout", {
111
133
  enumerable: true,
112
134
  configurable: true,
@@ -192,7 +214,12 @@ class CommandHandle {
192
214
  return;
193
215
  for await (const msg of this._stream) {
194
216
  const msgType = msg.type;
195
- if (msgType === "stdout" || msgType === "stderr") {
217
+ if (msgType === "started") {
218
+ if (acknowledgesReconnect(msg, this._commandId)) {
219
+ this._reconnectAttempts = 0;
220
+ }
221
+ }
222
+ else if (msgType === "stdout" || msgType === "stderr") {
196
223
  const chunk = {
197
224
  stream: msgType,
198
225
  data: msg.data,
@@ -227,11 +254,11 @@ class CommandHandle {
227
254
  * - After kill(): no reconnect, error propagates
228
255
  */
229
256
  async *[Symbol.asyncIterator]() {
230
- let reconnectAttempts = 0;
257
+ this._reconnectAttempts = 0;
231
258
  while (true) {
232
259
  try {
233
260
  for await (const chunk of this._iterStream()) {
234
- reconnectAttempts = 0; // Reset on successful data
261
+ this._reconnectAttempts = 0; // Reset on successful data
235
262
  if (chunk.stream === "stdout") {
236
263
  this._lastStdoutOffset =
237
264
  chunk.offset + new TextEncoder().encode(chunk.data).length;
@@ -247,21 +274,21 @@ class CommandHandle {
247
274
  return; // Stream ended normally (exit message received)
248
275
  }
249
276
  catch (e) {
250
- const eName = e != null && typeof e === "object" ? e.name : "";
251
- if (eName !== "LangSmithSandboxConnectionError" &&
252
- eName !== "LangSmithSandboxServerReloadError") {
277
+ // instanceof, not a name check: subclasses (connect timeout, server
278
+ // reload) must stay retryable here.
279
+ if (!(e instanceof errors_js_1.LangSmithSandboxConnectionError)) {
253
280
  throw e;
254
281
  }
255
282
  if (this._control && this._control.killed) {
256
283
  throw e;
257
284
  }
258
- reconnectAttempts++;
259
- if (reconnectAttempts > CommandHandle.MAX_AUTO_RECONNECTS) {
260
- throw new errors_js_1.LangSmithSandboxConnectionError(`Lost connection ${reconnectAttempts} times in succession, giving up`);
285
+ this._reconnectAttempts++;
286
+ if (this._reconnectAttempts > CommandHandle.MAX_AUTO_RECONNECTS) {
287
+ throw new errors_js_1.LangSmithSandboxConnectionError(`Failed to reattach to the command ${this._reconnectAttempts} times in succession, giving up`);
261
288
  }
262
- const isHotReload = eName === "LangSmithSandboxServerReloadError";
289
+ const isHotReload = e instanceof errors_js_1.LangSmithSandboxServerReloadError;
263
290
  if (!isHotReload) {
264
- const delay = Math.min(CommandHandle.BACKOFF_BASE * 2 ** (reconnectAttempts - 1), CommandHandle.BACKOFF_MAX);
291
+ const delay = Math.min(CommandHandle.BACKOFF_BASE * 2 ** (this._reconnectAttempts - 1), CommandHandle.BACKOFF_MAX);
265
292
  await new Promise((r) => setTimeout(r, delay * 1000));
266
293
  }
267
294
  if (this._commandId === null) {
@@ -45,6 +45,7 @@ export declare class CommandHandle {
45
45
  private _lastStdoutOffset;
46
46
  private _lastStderrOffset;
47
47
  private _started;
48
+ private _reconnectAttempts;
48
49
  private _onStdout?;
49
50
  private _onStderr?;
50
51
  /**
@@ -4,7 +4,23 @@
4
4
  *
5
5
  * Port of Python's AsyncCommandHandle to TypeScript.
6
6
  */
7
- import { LangSmithSandboxConnectionError, LangSmithSandboxOperationError, LangSmithStreamEndedBeforeStartedError, } from "./errors.js";
7
+ import { LangSmithSandboxConnectionError, LangSmithSandboxServerReloadError, LangSmithSandboxOperationError, LangSmithStreamEndedBeforeStartedError, } from "./errors.js";
8
+ /**
9
+ * Whether a 'started' frame proves *this* command was reattached to.
10
+ *
11
+ * A command and its WebSocket are separate things: the command keeps running on
12
+ * the server and the socket is only this client's attachment to it. The server
13
+ * acknowledges every successful reattachment with 'started', which for a command
14
+ * that produces no output is the only evidence it landed. One naming a different
15
+ * command is not evidence, and must not clear the reconnect budget.
16
+ */
17
+ function acknowledgesReconnect(msg, commandId) {
18
+ const acked = msg.command_id;
19
+ if (acked === commandId)
20
+ return true;
21
+ console.warn(`Ignoring reconnect acknowledgement for command ${String(acked)} while attached to ${String(commandId)}`);
22
+ return false;
23
+ }
8
24
  /**
9
25
  * Async handle to a running command with streaming output and auto-reconnect.
10
26
  *
@@ -104,6 +120,12 @@ export class CommandHandle {
104
120
  writable: true,
105
121
  value: void 0
106
122
  });
123
+ Object.defineProperty(this, "_reconnectAttempts", {
124
+ enumerable: true,
125
+ configurable: true,
126
+ writable: true,
127
+ value: 0
128
+ });
107
129
  Object.defineProperty(this, "_onStdout", {
108
130
  enumerable: true,
109
131
  configurable: true,
@@ -189,7 +211,12 @@ export class CommandHandle {
189
211
  return;
190
212
  for await (const msg of this._stream) {
191
213
  const msgType = msg.type;
192
- if (msgType === "stdout" || msgType === "stderr") {
214
+ if (msgType === "started") {
215
+ if (acknowledgesReconnect(msg, this._commandId)) {
216
+ this._reconnectAttempts = 0;
217
+ }
218
+ }
219
+ else if (msgType === "stdout" || msgType === "stderr") {
193
220
  const chunk = {
194
221
  stream: msgType,
195
222
  data: msg.data,
@@ -224,11 +251,11 @@ export class CommandHandle {
224
251
  * - After kill(): no reconnect, error propagates
225
252
  */
226
253
  async *[Symbol.asyncIterator]() {
227
- let reconnectAttempts = 0;
254
+ this._reconnectAttempts = 0;
228
255
  while (true) {
229
256
  try {
230
257
  for await (const chunk of this._iterStream()) {
231
- reconnectAttempts = 0; // Reset on successful data
258
+ this._reconnectAttempts = 0; // Reset on successful data
232
259
  if (chunk.stream === "stdout") {
233
260
  this._lastStdoutOffset =
234
261
  chunk.offset + new TextEncoder().encode(chunk.data).length;
@@ -244,21 +271,21 @@ export class CommandHandle {
244
271
  return; // Stream ended normally (exit message received)
245
272
  }
246
273
  catch (e) {
247
- const eName = e != null && typeof e === "object" ? e.name : "";
248
- if (eName !== "LangSmithSandboxConnectionError" &&
249
- eName !== "LangSmithSandboxServerReloadError") {
274
+ // instanceof, not a name check: subclasses (connect timeout, server
275
+ // reload) must stay retryable here.
276
+ if (!(e instanceof LangSmithSandboxConnectionError)) {
250
277
  throw e;
251
278
  }
252
279
  if (this._control && this._control.killed) {
253
280
  throw e;
254
281
  }
255
- reconnectAttempts++;
256
- if (reconnectAttempts > CommandHandle.MAX_AUTO_RECONNECTS) {
257
- throw new LangSmithSandboxConnectionError(`Lost connection ${reconnectAttempts} times in succession, giving up`);
282
+ this._reconnectAttempts++;
283
+ if (this._reconnectAttempts > CommandHandle.MAX_AUTO_RECONNECTS) {
284
+ throw new LangSmithSandboxConnectionError(`Failed to reattach to the command ${this._reconnectAttempts} times in succession, giving up`);
258
285
  }
259
- const isHotReload = eName === "LangSmithSandboxServerReloadError";
286
+ const isHotReload = e instanceof LangSmithSandboxServerReloadError;
260
287
  if (!isHotReload) {
261
- const delay = Math.min(CommandHandle.BACKOFF_BASE * 2 ** (reconnectAttempts - 1), CommandHandle.BACKOFF_MAX);
288
+ const delay = Math.min(CommandHandle.BACKOFF_BASE * 2 ** (this._reconnectAttempts - 1), CommandHandle.BACKOFF_MAX);
262
289
  await new Promise((r) => setTimeout(r, delay * 1000));
263
290
  }
264
291
  if (this._commandId === null) {
@@ -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.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.LangSmithSandboxConnectionError = exports.LangSmithSandboxAuthenticationError = exports.LangSmithSandboxAPIError = exports.LangSmithSandboxError = void 0;
11
11
  /**
12
12
  * Base exception for sandbox client errors.
13
13
  */
@@ -372,6 +372,21 @@ class LangSmithStreamEndedBeforeStartedError extends LangSmithSandboxOperationEr
372
372
  }
373
373
  }
374
374
  exports.LangSmithStreamEndedBeforeStartedError = LangSmithStreamEndedBeforeStartedError;
375
+ /**
376
+ * Thrown when the socket fails or times out before the WebSocket handshake.
377
+ *
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.
382
+ */
383
+ class LangSmithSandboxConnectTimeoutError extends LangSmithSandboxConnectionError {
384
+ constructor(message) {
385
+ super(message);
386
+ this.name = "LangSmithSandboxConnectTimeoutError";
387
+ }
388
+ }
389
+ exports.LangSmithSandboxConnectTimeoutError = LangSmithSandboxConnectTimeoutError;
375
390
  /**
376
391
  * Raised when the sandbox server is reloading (close code 1001).
377
392
  *
@@ -165,6 +165,17 @@ export declare class LangSmithCommandTimeoutError extends LangSmithSandboxOperat
165
165
  export declare class LangSmithStreamEndedBeforeStartedError extends LangSmithSandboxOperationError {
166
166
  constructor(message: string);
167
167
  }
168
+ /**
169
+ * Thrown when the socket fails or times out before the WebSocket handshake.
170
+ *
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.
175
+ */
176
+ export declare class LangSmithSandboxConnectTimeoutError extends LangSmithSandboxConnectionError {
177
+ constructor(message: string);
178
+ }
168
179
  /**
169
180
  * Raised when the sandbox server is reloading (close code 1001).
170
181
  *
@@ -351,6 +351,20 @@ export class LangSmithStreamEndedBeforeStartedError extends LangSmithSandboxOper
351
351
  this.name = "LangSmithStreamEndedBeforeStartedError";
352
352
  }
353
353
  }
354
+ /**
355
+ * Thrown when the socket fails or times out before the WebSocket handshake.
356
+ *
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.
361
+ */
362
+ export class LangSmithSandboxConnectTimeoutError extends LangSmithSandboxConnectionError {
363
+ constructor(message) {
364
+ super(message);
365
+ this.name = "LangSmithSandboxConnectTimeoutError";
366
+ }
367
+ }
354
368
  /**
355
369
  * Raised when the sandbox server is reloading (close code 1001).
356
370
  *
@@ -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.LangSmithSandboxConnectionError = exports.LangSmithSandboxAuthenticationError = exports.LangSmithSandboxAPIError = exports.LangSmithSandboxError = exports.s3Mount = exports.mountConfig = exports.gitMount = exports.gcsMount = 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.LangSmithSandboxConnectionError = exports.LangSmithSandboxAuthenticationError = exports.LangSmithSandboxAPIError = exports.LangSmithSandboxError = exports.s3Mount = exports.mountConfig = exports.gitMount = exports.gcsMount = 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; } });
@@ -55,6 +55,7 @@ Object.defineProperty(exports, "LangSmithSandboxError", { enumerable: true, get:
55
55
  Object.defineProperty(exports, "LangSmithSandboxAPIError", { enumerable: true, get: function () { return errors_js_1.LangSmithSandboxAPIError; } });
56
56
  Object.defineProperty(exports, "LangSmithSandboxAuthenticationError", { enumerable: true, get: function () { return errors_js_1.LangSmithSandboxAuthenticationError; } });
57
57
  Object.defineProperty(exports, "LangSmithSandboxConnectionError", { enumerable: true, get: function () { return errors_js_1.LangSmithSandboxConnectionError; } });
58
+ Object.defineProperty(exports, "LangSmithSandboxConnectTimeoutError", { enumerable: true, get: function () { return errors_js_1.LangSmithSandboxConnectTimeoutError; } });
58
59
  Object.defineProperty(exports, "LangSmithSandboxServerReloadError", { enumerable: true, get: function () { return errors_js_1.LangSmithSandboxServerReloadError; } });
59
60
  // Resource errors (type-based with resourceType attribute)
60
61
  Object.defineProperty(exports, "LangSmithResourceNotFoundError", { enumerable: true, get: function () { return errors_js_1.LangSmithResourceNotFoundError; } });
@@ -33,4 +33,4 @@ export { CommandHandle } from "./command_handle.js";
33
33
  export { awsAuth, gcpAuth, opaqueSecret, proxyConfig, workspaceSecret, } from "./proxy_config.js";
34
34
  export { gcsMount, gitMount, mountConfig, s3Mount } from "./mounts.js";
35
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, GCSMountConfig, GCSMountSpec, GitMountConfig, GitMountRefSpec, GitMountSpec, S3MountConfig, S3MountSpec, CreateSnapshotOptions, CreateDockerfileSnapshotOptions, CaptureSnapshotOptions, ListSnapshotsOptions, WaitForSnapshotOptions, StartSandboxOptions, UpdateSandboxOptions, WaitForSandboxOptions, } from "./types.js";
36
- export { LangSmithSandboxError, LangSmithSandboxAPIError, LangSmithSandboxAuthenticationError, LangSmithSandboxConnectionError, LangSmithSandboxServerReloadError, LangSmithResourceNotFoundError, LangSmithResourceTimeoutError, LangSmithResourceInUseError, LangSmithResourceAlreadyExistsError, LangSmithResourceNameConflictError, LangSmithValidationError, LangSmithQuotaExceededError, LangSmithResourceCreationError, LangSmithSandboxCreationError, LangSmithSandboxNotReadyError, LangSmithSandboxOperationError, LangSmithCommandTimeoutError, LangSmithDataplaneNotConfiguredError, } from "./errors.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";
@@ -36,7 +36,7 @@ export { gcsMount, gitMount, mountConfig, s3Mount } from "./mounts.js";
36
36
  // Errors
37
37
  export {
38
38
  // Base and connection errors
39
- LangSmithSandboxError, LangSmithSandboxAPIError, LangSmithSandboxAuthenticationError, LangSmithSandboxConnectionError, LangSmithSandboxServerReloadError,
39
+ LangSmithSandboxError, LangSmithSandboxAPIError, LangSmithSandboxAuthenticationError, LangSmithSandboxConnectionError, LangSmithSandboxConnectTimeoutError, LangSmithSandboxServerReloadError,
40
40
  // Resource errors (type-based with resourceType attribute)
41
41
  LangSmithResourceNotFoundError, LangSmithResourceTimeoutError, LangSmithResourceInUseError, LangSmithResourceAlreadyExistsError, LangSmithResourceNameConflictError,
42
42
  // Validation and quota errors
@@ -234,6 +234,7 @@ class Sandbox {
234
234
  // spawning a second one.
235
235
  const execCommandId = commandId ?? (0, uuid_js_1.uuid7)();
236
236
  let attempt = 0;
237
+ const deadline = (0, ws_execute_js_1.connectDeadline)();
237
238
  // eslint-disable-next-line no-constant-condition
238
239
  while (true) {
239
240
  const [stream, control] = await (0, ws_execute_js_1.runWsStream)(dataplaneUrl, this._client.getApiKey(), command, {
@@ -246,6 +247,7 @@ class Sandbox {
246
247
  killOnDisconnect,
247
248
  ttlSeconds,
248
249
  pty,
250
+ openTimeout: (0, ws_execute_js_1.openTimeoutFor)(deadline),
249
251
  ...(Object.keys(clientHeaders).length > 0
250
252
  ? { headers: clientHeaders }
251
253
  : {}),
@@ -259,16 +261,31 @@ class Sandbox {
259
261
  return handle;
260
262
  }
261
263
  catch (e) {
262
- // Idempotent re-issue (same command_id) after an early close.
263
- if (!(e instanceof errors_js_1.LangSmithStreamEndedBeforeStartedError)) {
264
+ // Idempotent re-issue (same command_id): neither an early close nor a
265
+ // failed connect can have started a second command.
266
+ if (!(e instanceof errors_js_1.LangSmithStreamEndedBeforeStartedError) &&
267
+ !(e instanceof errors_js_1.LangSmithSandboxConnectTimeoutError)) {
264
268
  throw e;
265
269
  }
266
270
  attempt++;
267
271
  if (attempt > command_handle_js_1.CommandHandle.MAX_AUTO_RECONNECTS) {
268
272
  throw e;
269
273
  }
270
- const delay = Math.min(command_handle_js_1.CommandHandle.BACKOFF_BASE * 2 ** (attempt - 1), command_handle_js_1.CommandHandle.BACKOFF_MAX);
274
+ let delay = Math.min(command_handle_js_1.CommandHandle.BACKOFF_BASE * 2 ** (attempt - 1), command_handle_js_1.CommandHandle.BACKOFF_MAX);
275
+ const remaining = (0, ws_execute_js_1.remainingBudget)(deadline);
276
+ if (remaining !== undefined) {
277
+ if (remaining <= 0) {
278
+ throw e;
279
+ }
280
+ delay = Math.min(delay, remaining);
281
+ }
271
282
  await new Promise((r) => setTimeout(r, delay * 1000));
283
+ // Never start an attempt with no budget left: openTimeoutFor would
284
+ // return 0, which ws forwards to Node as "no timeout at all".
285
+ const left = (0, ws_execute_js_1.remainingBudget)(deadline);
286
+ if (left !== undefined && left <= 0) {
287
+ throw e;
288
+ }
272
289
  }
273
290
  }
274
291
  }
@@ -426,8 +443,9 @@ class Sandbox {
426
443
  */
427
444
  async stop() {
428
445
  await this._client.stopSandbox(this.name);
446
+ // dataplane_url stays set: it is stable across stop/start and a request on
447
+ // it resumes the sandbox.
429
448
  this.status = "stopped";
430
- this.dataplane_url = undefined;
431
449
  }
432
450
  /**
433
451
  * Capture a snapshot from this sandbox.
@@ -2,10 +2,10 @@
2
2
  * Sandbox class for interacting with a specific sandbox instance.
3
3
  */
4
4
  import { uuid7 } from "../uuid.js";
5
- import { LangSmithDataplaneNotConfiguredError, LangSmithStreamEndedBeforeStartedError, } from "./errors.js";
5
+ import { LangSmithDataplaneNotConfiguredError, LangSmithSandboxConnectTimeoutError, LangSmithStreamEndedBeforeStartedError, } from "./errors.js";
6
6
  import { handleSandboxHttpError } from "./helpers.js";
7
7
  import { CommandHandle } from "./command_handle.js";
8
- import { isWsAvailable, reconnectWsStream, runWsStream } from "./ws_execute.js";
8
+ import { connectDeadline, isWsAvailable, openTimeoutFor, reconnectWsStream, remainingBudget, runWsStream, } from "./ws_execute.js";
9
9
  /**
10
10
  * Represents an active sandbox for running commands and file operations.
11
11
  *
@@ -231,6 +231,7 @@ export class Sandbox {
231
231
  // spawning a second one.
232
232
  const execCommandId = commandId ?? uuid7();
233
233
  let attempt = 0;
234
+ const deadline = connectDeadline();
234
235
  // eslint-disable-next-line no-constant-condition
235
236
  while (true) {
236
237
  const [stream, control] = await runWsStream(dataplaneUrl, this._client.getApiKey(), command, {
@@ -243,6 +244,7 @@ export class Sandbox {
243
244
  killOnDisconnect,
244
245
  ttlSeconds,
245
246
  pty,
247
+ openTimeout: openTimeoutFor(deadline),
246
248
  ...(Object.keys(clientHeaders).length > 0
247
249
  ? { headers: clientHeaders }
248
250
  : {}),
@@ -256,16 +258,31 @@ export class Sandbox {
256
258
  return handle;
257
259
  }
258
260
  catch (e) {
259
- // Idempotent re-issue (same command_id) after an early close.
260
- if (!(e instanceof LangSmithStreamEndedBeforeStartedError)) {
261
+ // Idempotent re-issue (same command_id): neither an early close nor a
262
+ // failed connect can have started a second command.
263
+ if (!(e instanceof LangSmithStreamEndedBeforeStartedError) &&
264
+ !(e instanceof LangSmithSandboxConnectTimeoutError)) {
261
265
  throw e;
262
266
  }
263
267
  attempt++;
264
268
  if (attempt > CommandHandle.MAX_AUTO_RECONNECTS) {
265
269
  throw e;
266
270
  }
267
- const delay = Math.min(CommandHandle.BACKOFF_BASE * 2 ** (attempt - 1), CommandHandle.BACKOFF_MAX);
271
+ let delay = Math.min(CommandHandle.BACKOFF_BASE * 2 ** (attempt - 1), CommandHandle.BACKOFF_MAX);
272
+ const remaining = remainingBudget(deadline);
273
+ if (remaining !== undefined) {
274
+ if (remaining <= 0) {
275
+ throw e;
276
+ }
277
+ delay = Math.min(delay, remaining);
278
+ }
268
279
  await new Promise((r) => setTimeout(r, delay * 1000));
280
+ // Never start an attempt with no budget left: openTimeoutFor would
281
+ // return 0, which ws forwards to Node as "no timeout at all".
282
+ const left = remainingBudget(deadline);
283
+ if (left !== undefined && left <= 0) {
284
+ throw e;
285
+ }
269
286
  }
270
287
  }
271
288
  }
@@ -423,8 +440,9 @@ export class Sandbox {
423
440
  */
424
441
  async stop() {
425
442
  await this._client.stopSandbox(this.name);
443
+ // dataplane_url stays set: it is stable across stop/start and a request on
444
+ // it resumes the sandbox.
426
445
  this.status = "stopped";
427
- this.dataplane_url = undefined;
428
446
  }
429
447
  /**
430
448
  * Capture a snapshot from this sandbox.
@@ -6,7 +6,10 @@
6
6
  * Install with: npm install ws
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.WSStreamControl = void 0;
9
+ exports.WSStreamControl = exports.WS_CONNECT_BUDGET = exports.WS_OPEN_TIMEOUT = void 0;
10
+ exports.connectDeadline = connectDeadline;
11
+ exports.remainingBudget = remainingBudget;
12
+ exports.openTimeoutFor = openTimeoutFor;
10
13
  exports.isWsAvailable = isWsAvailable;
11
14
  exports.buildWsUrl = buildWsUrl;
12
15
  exports.buildAuthHeaders = buildAuthHeaders;
@@ -14,6 +17,46 @@ exports.raiseForWsError = raiseForWsError;
14
17
  exports.runWsStream = runWsStream;
15
18
  exports.reconnectWsStream = reconnectWsStream;
16
19
  const errors_js_1 = require("./errors.cjs");
20
+ const env_js_1 = require("../utils/env.cjs");
21
+ /** Read a WebSocket timeout override, in seconds. `<= 0` disables it. */
22
+ function envTimeout(name, defaultSeconds) {
23
+ const raw = (0, env_js_1.getLangSmithEnvironmentVariable)(name);
24
+ if (raw === undefined || raw.trim() === "")
25
+ return defaultSeconds;
26
+ const value = Number(raw);
27
+ if (!Number.isFinite(value)) {
28
+ console.warn(`Ignoring invalid LANGSMITH_${name}=${raw}, using ${defaultSeconds}`);
29
+ return defaultSeconds;
30
+ }
31
+ return value > 0 ? value : undefined;
32
+ }
33
+ // A slow cold start is recovered by the retry loop in run(), not by waiting here.
34
+ exports.WS_OPEN_TIMEOUT = envTimeout("SANDBOX_WS_TIMEOUT_OPEN", 30);
35
+ // Ceiling on the whole connect phase. Without it, retrying a blackholed
36
+ // handshake costs MAX_AUTO_RECONNECTS + 1 full open timeouts plus backoff.
37
+ exports.WS_CONNECT_BUDGET = envTimeout("SANDBOX_WS_TIMEOUT_CONNECT_BUDGET", 120);
38
+ function nowSeconds() {
39
+ return performance.now() / 1000;
40
+ }
41
+ /** Instant after which connect attempts must stop, if bounded. */
42
+ function connectDeadline() {
43
+ return exports.WS_CONNECT_BUDGET === undefined
44
+ ? undefined
45
+ : nowSeconds() + exports.WS_CONNECT_BUDGET;
46
+ }
47
+ /** Seconds left in the connect budget, or undefined when unbounded. */
48
+ function remainingBudget(deadline) {
49
+ return deadline === undefined ? undefined : deadline - nowSeconds();
50
+ }
51
+ /** Per-attempt open timeout, clamped so it cannot outlive `deadline`. */
52
+ function openTimeoutFor(deadline) {
53
+ if (deadline === undefined)
54
+ return exports.WS_OPEN_TIMEOUT;
55
+ const remaining = Math.max(deadline - nowSeconds(), 0);
56
+ return exports.WS_OPEN_TIMEOUT === undefined
57
+ ? remaining
58
+ : Math.min(exports.WS_OPEN_TIMEOUT, remaining);
59
+ }
17
60
  // Load the optional 'ws' package once, at module load, instead of importing on
18
61
  // every call. Resolves to the WebSocket constructor, or null if 'ws' isn't
19
62
  // installed. run() reads isWsAvailable() to choose WS vs the HTTP fallback.
@@ -140,18 +183,42 @@ function raiseForWsError(msg, commandId = "") {
140
183
  /**
141
184
  * Create a ws WebSocket connection and return a promise that resolves when open
142
185
  * or rejects on error.
186
+ *
187
+ * A socket-level failure rejects with the retryable connect-timeout error; a
188
+ * non-101 response rejects with the permanent connection error.
143
189
  */
144
- async function connectWs(url, headers) {
190
+ async function connectWs(url, headers, openTimeout = exports.WS_OPEN_TIMEOUT) {
145
191
  const { WebSocket: WS } = await ensureWs();
146
192
  return new Promise((resolve, reject) => {
147
- const ws = new WS(url, { headers });
193
+ const ws = new WS(url, {
194
+ headers,
195
+ handshakeTimeout: openTimeout === undefined ? undefined : openTimeout * 1000,
196
+ });
197
+ let settled = false;
148
198
  ws.on("open", () => {
149
- ws.removeAllListeners("error");
199
+ if (settled)
200
+ return;
201
+ settled = true;
150
202
  resolve(ws);
151
203
  });
204
+ // Attaching this suppresses ws's own "error" for a non-101 response, so the
205
+ // rejected upgrade cannot be misreported as a retryable socket failure. ws
206
+ // leaves cleanup to the handler once a listener is present.
207
+ ws.on("unexpected-response", (req, res) => {
208
+ res.resume?.();
209
+ req.destroy?.();
210
+ if (settled)
211
+ return;
212
+ settled = true;
213
+ reject(new errors_js_1.LangSmithSandboxConnectionError(`WebSocket upgrade to ${url} rejected by server (HTTP ${res.statusCode})`));
214
+ });
215
+ // Stays attached after settling so a late error has a listener and cannot
216
+ // become an unhandled 'error' event.
152
217
  ws.on("error", (err) => {
153
- ws.removeAllListeners("open");
154
- reject(new errors_js_1.LangSmithSandboxConnectionError(`Failed to connect to sandbox WebSocket: ${err.message}`));
218
+ if (settled)
219
+ return;
220
+ settled = true;
221
+ reject(new errors_js_1.LangSmithSandboxConnectTimeoutError(`Failed to connect to sandbox WebSocket: ${err.message}`));
155
222
  });
156
223
  });
157
224
  }
@@ -249,14 +316,14 @@ async function* readWsMessages(ws) {
249
316
  * data arrives in addition to yielding the messages.
250
317
  */
251
318
  async function runWsStream(dataplaneUrl, apiKey, command, options = {}) {
252
- const { timeout = 60, env, cwd, shell = "/bin/bash", onStdout, onStderr, commandId, idleTimeout = 300, killOnDisconnect = false, ttlSeconds = 600, pty, headers: extraHeaders, } = options;
319
+ const { timeout = 60, env, cwd, shell = "/bin/bash", onStdout, onStderr, commandId, idleTimeout = 300, killOnDisconnect = false, ttlSeconds = 600, pty, headers: extraHeaders, openTimeout = exports.WS_OPEN_TIMEOUT, } = options;
253
320
  const wsUrl = buildWsUrl(dataplaneUrl);
254
321
  const headers = buildAuthHeaders(apiKey, extraHeaders);
255
322
  const control = new WSStreamControl();
256
323
  async function* stream() {
257
324
  let ws;
258
325
  try {
259
- ws = await connectWs(wsUrl, headers);
326
+ ws = await connectWs(wsUrl, headers, openTimeout);
260
327
  control._bind(ws);
261
328
  // Send execute request
262
329
  const payload = {
@@ -315,8 +382,10 @@ async function runWsStream(dataplaneUrl, apiKey, command, options = {}) {
315
382
  * Reconnect to an existing command over WebSocket.
316
383
  *
317
384
  * Returns a tuple of [async_message_iterator, control], same as runWsStream.
318
- * The iterator yields stdout, stderr, exit, and error messages.
319
- * No 'started' message is sent on reconnection.
385
+ * The iterator yields the server's 'started' acknowledgement, then stdout,
386
+ * stderr, exit, and error messages. That acknowledgement is the only evidence a
387
+ * reattachment landed for a command producing no output, so it is forwarded
388
+ * rather than dropped.
320
389
  */
321
390
  async function reconnectWsStream(dataplaneUrl, apiKey, commandId, options = {}) {
322
391
  const { stdoutOffset = 0, stderrOffset = 0, headers: extraHeaders } = options;
@@ -338,7 +407,9 @@ async function reconnectWsStream(dataplaneUrl, apiKey, commandId, options = {})
338
407
  // Read messages until exit or error
339
408
  for await (const msg of readWsMessages(ws)) {
340
409
  const msgType = msg.type;
341
- if (msgType === "stdout" || msgType === "stderr") {
410
+ if (msgType === "started" ||
411
+ msgType === "stdout" ||
412
+ msgType === "stderr") {
342
413
  yield msg;
343
414
  }
344
415
  else if (msgType === "exit") {