langsmith 0.5.9 → 0.5.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.cjs CHANGED
@@ -50,6 +50,26 @@ const index_js_2 = require("./utils/prompt_cache/index.cjs");
50
50
  const fsUtils = __importStar(require("./utils/fs.cjs"));
51
51
  const fetch_js_1 = require("./singletons/fetch.cjs");
52
52
  const index_js_3 = require("./utils/fast-safe-stringify/index.cjs");
53
+ /**
54
+ * Catches timestamps without a timezone suffix.
55
+ */
56
+ function _ensureUTCTimestamp(ts) {
57
+ if (typeof ts === "string" &&
58
+ ts.length > 0 &&
59
+ !ts.includes("Z") &&
60
+ !ts.includes("+") &&
61
+ !ts.includes("-", 10)) {
62
+ return ts + "Z";
63
+ }
64
+ return ts;
65
+ }
66
+ function _normalizeRunTimestamps(run) {
67
+ return {
68
+ ...run,
69
+ start_time: _ensureUTCTimestamp(run.start_time),
70
+ end_time: _ensureUTCTimestamp(run.end_time),
71
+ };
72
+ }
53
73
  function mergeRuntimeEnvIntoRun(run, cachedEnvVars, omitTracedRuntimeInfo) {
54
74
  if (omitTracedRuntimeInfo) {
55
75
  return run;
@@ -1542,7 +1562,7 @@ class Client {
1542
1562
  }
1543
1563
  async readRun(runId, { loadChildRuns } = { loadChildRuns: false }) {
1544
1564
  (0, _uuid_js_1.assertUuid)(runId);
1545
- let run = await this._get(`/runs/${runId}`);
1565
+ let run = _normalizeRunTimestamps(await this._get(`/runs/${runId}`));
1546
1566
  if (loadChildRuns) {
1547
1567
  run = await this._loadChildRuns(run);
1548
1568
  }
@@ -1761,20 +1781,21 @@ class Client {
1761
1781
  }
1762
1782
  let runsYielded = 0;
1763
1783
  for await (const runs of this._getCursorPaginatedList("/runs/query", body)) {
1784
+ const normalized = runs.map(_normalizeRunTimestamps);
1764
1785
  if (limit) {
1765
1786
  if (runsYielded >= limit) {
1766
1787
  break;
1767
1788
  }
1768
- if (runs.length + runsYielded > limit) {
1769
- const newRuns = runs.slice(0, limit - runsYielded);
1789
+ if (normalized.length + runsYielded > limit) {
1790
+ const newRuns = normalized.slice(0, limit - runsYielded);
1770
1791
  yield* newRuns;
1771
1792
  break;
1772
1793
  }
1773
- runsYielded += runs.length;
1774
- yield* runs;
1794
+ runsYielded += normalized.length;
1795
+ yield* normalized;
1775
1796
  }
1776
1797
  else {
1777
- yield* runs;
1798
+ yield* normalized;
1778
1799
  }
1779
1800
  }
1780
1801
  }
@@ -1891,7 +1912,8 @@ class Client {
1891
1912
  }
1892
1913
  const threadsMap = new Map();
1893
1914
  for await (const runs of this._getCursorPaginatedList("/runs/query", bodyQuery)) {
1894
- for (const run of runs) {
1915
+ for (const raw of runs) {
1916
+ const run = _normalizeRunTimestamps(raw);
1895
1917
  const tid = run.thread_id;
1896
1918
  if (tid) {
1897
1919
  const list = threadsMap.get(tid) ?? [];
@@ -2064,8 +2086,8 @@ class Client {
2064
2086
  await (0, error_js_1.raiseForStatus)(res, "list shared runs");
2065
2087
  return res;
2066
2088
  });
2067
- const runs = await response.json();
2068
- return runs;
2089
+ const runs = (await response.json());
2090
+ return runs.map(_normalizeRunTimestamps);
2069
2091
  }
2070
2092
  async readDatasetSharedSchema(datasetId, datasetName) {
2071
2093
  if (!datasetId && !datasetName) {
@@ -3714,7 +3736,8 @@ class Client {
3714
3736
  await (0, error_js_1.raiseForStatus)(res, "get run from annotation queue");
3715
3737
  return res;
3716
3738
  });
3717
- return response.json();
3739
+ const run = await response.json();
3740
+ return _normalizeRunTimestamps(run);
3718
3741
  }
3719
3742
  /**
3720
3743
  * Delete a run from an an annotation queue.
package/dist/client.js CHANGED
@@ -13,6 +13,26 @@ import { promptCacheSingleton, } from "./utils/prompt_cache/index.js";
13
13
  import * as fsUtils from "./utils/fs.js";
14
14
  import { _shouldStreamForGlobalFetchImplementation, _getFetchImplementation, } from "./singletons/fetch.js";
15
15
  import { serialize as serializePayloadForTracing } from "./utils/fast-safe-stringify/index.js";
16
+ /**
17
+ * Catches timestamps without a timezone suffix.
18
+ */
19
+ function _ensureUTCTimestamp(ts) {
20
+ if (typeof ts === "string" &&
21
+ ts.length > 0 &&
22
+ !ts.includes("Z") &&
23
+ !ts.includes("+") &&
24
+ !ts.includes("-", 10)) {
25
+ return ts + "Z";
26
+ }
27
+ return ts;
28
+ }
29
+ function _normalizeRunTimestamps(run) {
30
+ return {
31
+ ...run,
32
+ start_time: _ensureUTCTimestamp(run.start_time),
33
+ end_time: _ensureUTCTimestamp(run.end_time),
34
+ };
35
+ }
16
36
  export function mergeRuntimeEnvIntoRun(run, cachedEnvVars, omitTracedRuntimeInfo) {
17
37
  if (omitTracedRuntimeInfo) {
18
38
  return run;
@@ -1504,7 +1524,7 @@ export class Client {
1504
1524
  }
1505
1525
  async readRun(runId, { loadChildRuns } = { loadChildRuns: false }) {
1506
1526
  assertUuid(runId);
1507
- let run = await this._get(`/runs/${runId}`);
1527
+ let run = _normalizeRunTimestamps(await this._get(`/runs/${runId}`));
1508
1528
  if (loadChildRuns) {
1509
1529
  run = await this._loadChildRuns(run);
1510
1530
  }
@@ -1723,20 +1743,21 @@ export class Client {
1723
1743
  }
1724
1744
  let runsYielded = 0;
1725
1745
  for await (const runs of this._getCursorPaginatedList("/runs/query", body)) {
1746
+ const normalized = runs.map(_normalizeRunTimestamps);
1726
1747
  if (limit) {
1727
1748
  if (runsYielded >= limit) {
1728
1749
  break;
1729
1750
  }
1730
- if (runs.length + runsYielded > limit) {
1731
- const newRuns = runs.slice(0, limit - runsYielded);
1751
+ if (normalized.length + runsYielded > limit) {
1752
+ const newRuns = normalized.slice(0, limit - runsYielded);
1732
1753
  yield* newRuns;
1733
1754
  break;
1734
1755
  }
1735
- runsYielded += runs.length;
1736
- yield* runs;
1756
+ runsYielded += normalized.length;
1757
+ yield* normalized;
1737
1758
  }
1738
1759
  else {
1739
- yield* runs;
1760
+ yield* normalized;
1740
1761
  }
1741
1762
  }
1742
1763
  }
@@ -1853,7 +1874,8 @@ export class Client {
1853
1874
  }
1854
1875
  const threadsMap = new Map();
1855
1876
  for await (const runs of this._getCursorPaginatedList("/runs/query", bodyQuery)) {
1856
- for (const run of runs) {
1877
+ for (const raw of runs) {
1878
+ const run = _normalizeRunTimestamps(raw);
1857
1879
  const tid = run.thread_id;
1858
1880
  if (tid) {
1859
1881
  const list = threadsMap.get(tid) ?? [];
@@ -2026,8 +2048,8 @@ export class Client {
2026
2048
  await raiseForStatus(res, "list shared runs");
2027
2049
  return res;
2028
2050
  });
2029
- const runs = await response.json();
2030
- return runs;
2051
+ const runs = (await response.json());
2052
+ return runs.map(_normalizeRunTimestamps);
2031
2053
  }
2032
2054
  async readDatasetSharedSchema(datasetId, datasetName) {
2033
2055
  if (!datasetId && !datasetName) {
@@ -3676,7 +3698,8 @@ export class Client {
3676
3698
  await raiseForStatus(res, "get run from annotation queue");
3677
3699
  return res;
3678
3700
  });
3679
- return response.json();
3701
+ const run = await response.json();
3702
+ return _normalizeRunTimestamps(run);
3680
3703
  }
3681
3704
  /**
3682
3705
  * Delete a run from an an annotation queue.
@@ -106,6 +106,13 @@ class SandboxClient {
106
106
  headers,
107
107
  }));
108
108
  }
109
+ /**
110
+ * Get the API key for WebSocket authentication.
111
+ * @internal
112
+ */
113
+ getApiKey() {
114
+ return this._apiKey;
115
+ }
109
116
  // =========================================================================
110
117
  // Volume Operations
111
118
  // =========================================================================
@@ -103,6 +103,13 @@ export class SandboxClient {
103
103
  headers,
104
104
  }));
105
105
  }
106
+ /**
107
+ * Get the API key for WebSocket authentication.
108
+ * @internal
109
+ */
110
+ getApiKey() {
111
+ return this._apiKey;
112
+ }
106
113
  // =========================================================================
107
114
  // Volume Operations
108
115
  // =========================================================================
@@ -0,0 +1,325 @@
1
+ "use strict";
2
+ /**
3
+ * CommandHandle - async handle to a running command with streaming output
4
+ * and auto-reconnect.
5
+ *
6
+ * Port of Python's AsyncCommandHandle to TypeScript.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.CommandHandle = void 0;
10
+ const errors_js_1 = require("./errors.cjs");
11
+ /**
12
+ * Async handle to a running command with streaming output and auto-reconnect.
13
+ *
14
+ * Async iterable, yielding OutputChunk objects (stdout and stderr interleaved
15
+ * in arrival order). Access .result after iteration to get the full
16
+ * ExecutionResult.
17
+ *
18
+ * Auto-reconnect behavior:
19
+ * - Server hot-reload (1001 Going Away): reconnect immediately
20
+ * - Network error / unexpected close: reconnect with exponential backoff
21
+ * - User called kill(): do NOT reconnect (propagate error)
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * const handle = await sandbox.run("make build", { timeout: 600, wait: false });
26
+ *
27
+ * for await (const chunk of handle) { // auto-reconnects on transient errors
28
+ * process.stdout.write(chunk.data);
29
+ * }
30
+ *
31
+ * const result = await handle.result;
32
+ * console.log(`Exit code: ${result.exit_code}`);
33
+ * ```
34
+ */
35
+ class CommandHandle {
36
+ /** @internal */
37
+ constructor(messageStream, control, sandbox, options) {
38
+ Object.defineProperty(this, "_stream", {
39
+ enumerable: true,
40
+ configurable: true,
41
+ writable: true,
42
+ value: void 0
43
+ });
44
+ Object.defineProperty(this, "_control", {
45
+ enumerable: true,
46
+ configurable: true,
47
+ writable: true,
48
+ value: void 0
49
+ });
50
+ Object.defineProperty(this, "_sandbox", {
51
+ enumerable: true,
52
+ configurable: true,
53
+ writable: true,
54
+ value: void 0
55
+ });
56
+ Object.defineProperty(this, "_commandId", {
57
+ enumerable: true,
58
+ configurable: true,
59
+ writable: true,
60
+ value: null
61
+ });
62
+ Object.defineProperty(this, "_pid", {
63
+ enumerable: true,
64
+ configurable: true,
65
+ writable: true,
66
+ value: null
67
+ });
68
+ Object.defineProperty(this, "_result", {
69
+ enumerable: true,
70
+ configurable: true,
71
+ writable: true,
72
+ value: null
73
+ });
74
+ Object.defineProperty(this, "_stdoutParts", {
75
+ enumerable: true,
76
+ configurable: true,
77
+ writable: true,
78
+ value: []
79
+ });
80
+ Object.defineProperty(this, "_stderrParts", {
81
+ enumerable: true,
82
+ configurable: true,
83
+ writable: true,
84
+ value: []
85
+ });
86
+ Object.defineProperty(this, "_exhausted", {
87
+ enumerable: true,
88
+ configurable: true,
89
+ writable: true,
90
+ value: false
91
+ });
92
+ Object.defineProperty(this, "_lastStdoutOffset", {
93
+ enumerable: true,
94
+ configurable: true,
95
+ writable: true,
96
+ value: void 0
97
+ });
98
+ Object.defineProperty(this, "_lastStderrOffset", {
99
+ enumerable: true,
100
+ configurable: true,
101
+ writable: true,
102
+ value: void 0
103
+ });
104
+ Object.defineProperty(this, "_started", {
105
+ enumerable: true,
106
+ configurable: true,
107
+ writable: true,
108
+ value: void 0
109
+ });
110
+ this._stream = messageStream;
111
+ this._control = control;
112
+ this._sandbox = sandbox;
113
+ this._lastStdoutOffset = options?.stdoutOffset ?? 0;
114
+ this._lastStderrOffset = options?.stderrOffset ?? 0;
115
+ // New executions (no commandId): _ensureStarted reads "started".
116
+ // Reconnections (commandId set): skip since reconnect streams
117
+ // don't send a "started" message.
118
+ if (options?.commandId) {
119
+ this._commandId = options.commandId;
120
+ this._started = true;
121
+ }
122
+ else {
123
+ this._started = false;
124
+ }
125
+ }
126
+ /**
127
+ * Read the 'started' message to populate commandId and pid.
128
+ *
129
+ * Must be called (and awaited) before iterating for new executions.
130
+ */
131
+ async _ensureStarted() {
132
+ if (this._started)
133
+ return;
134
+ const firstResult = await this._stream.next();
135
+ if (firstResult.done) {
136
+ throw new errors_js_1.LangSmithSandboxOperationError("Command stream ended before 'started' message", "command");
137
+ }
138
+ const firstMsg = firstResult.value;
139
+ if (firstMsg.type !== "started") {
140
+ throw new errors_js_1.LangSmithSandboxOperationError(`Expected 'started' message, got '${firstMsg.type}'`, "command");
141
+ }
142
+ this._commandId = firstMsg.command_id ?? null;
143
+ this._pid = firstMsg.pid ?? null;
144
+ this._started = true;
145
+ }
146
+ /** The server-assigned command ID. Available after _ensureStarted(). */
147
+ get commandId() {
148
+ return this._commandId;
149
+ }
150
+ /** The process ID on the sandbox. Available after _ensureStarted(). */
151
+ get pid() {
152
+ return this._pid;
153
+ }
154
+ /**
155
+ * The final execution result. Drains the stream if not already exhausted.
156
+ */
157
+ get result() {
158
+ return this._getResult();
159
+ }
160
+ async _getResult() {
161
+ if (this._result === null) {
162
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
163
+ for await (const _ of this) {
164
+ // drain
165
+ }
166
+ }
167
+ if (this._result === null) {
168
+ throw new errors_js_1.LangSmithSandboxOperationError("Command stream ended without exit message", "command");
169
+ }
170
+ return this._result;
171
+ }
172
+ /**
173
+ * Iterate over output chunks from the current stream (no reconnect).
174
+ */
175
+ async *_iterStream() {
176
+ await this._ensureStarted();
177
+ if (this._exhausted)
178
+ return;
179
+ for await (const msg of this._stream) {
180
+ const msgType = msg.type;
181
+ if (msgType === "stdout" || msgType === "stderr") {
182
+ const chunk = {
183
+ stream: msgType,
184
+ data: msg.data,
185
+ offset: msg.offset ?? 0,
186
+ };
187
+ if (msgType === "stdout") {
188
+ this._stdoutParts.push(msg.data);
189
+ }
190
+ else {
191
+ this._stderrParts.push(msg.data);
192
+ }
193
+ yield chunk;
194
+ }
195
+ else if (msgType === "exit") {
196
+ this._result = {
197
+ stdout: this._stdoutParts.join(""),
198
+ stderr: this._stderrParts.join(""),
199
+ exit_code: msg.exit_code ?? -1,
200
+ };
201
+ this._exhausted = true;
202
+ return;
203
+ }
204
+ }
205
+ this._exhausted = true;
206
+ }
207
+ /**
208
+ * Async iterate over output chunks with auto-reconnect on transient errors.
209
+ *
210
+ * Reconnect strategy:
211
+ * - 1001 Going Away (hot-reload): immediate reconnect, no delay
212
+ * - Other SandboxConnectionError: exponential backoff (0.5s, 1s, 2s...)
213
+ * - After kill(): no reconnect, error propagates
214
+ */
215
+ async *[Symbol.asyncIterator]() {
216
+ let reconnectAttempts = 0;
217
+ while (true) {
218
+ try {
219
+ for await (const chunk of this._iterStream()) {
220
+ reconnectAttempts = 0; // Reset on successful data
221
+ if (chunk.stream === "stdout") {
222
+ this._lastStdoutOffset =
223
+ chunk.offset + new TextEncoder().encode(chunk.data).length;
224
+ }
225
+ else {
226
+ this._lastStderrOffset =
227
+ chunk.offset + new TextEncoder().encode(chunk.data).length;
228
+ }
229
+ yield chunk;
230
+ }
231
+ return; // Stream ended normally (exit message received)
232
+ }
233
+ catch (e) {
234
+ const eName = e != null && typeof e === "object" ? e.name : "";
235
+ if (eName !== "LangSmithSandboxConnectionError" &&
236
+ eName !== "LangSmithSandboxServerReloadError") {
237
+ throw e;
238
+ }
239
+ if (this._control && this._control.killed) {
240
+ throw e;
241
+ }
242
+ reconnectAttempts++;
243
+ if (reconnectAttempts > CommandHandle.MAX_AUTO_RECONNECTS) {
244
+ throw new errors_js_1.LangSmithSandboxConnectionError(`Lost connection ${reconnectAttempts} times in succession, giving up`);
245
+ }
246
+ const isHotReload = eName === "LangSmithSandboxServerReloadError";
247
+ if (!isHotReload) {
248
+ const delay = Math.min(CommandHandle.BACKOFF_BASE * 2 ** (reconnectAttempts - 1), CommandHandle.BACKOFF_MAX);
249
+ await new Promise((r) => setTimeout(r, delay * 1000));
250
+ }
251
+ if (this._commandId === null) {
252
+ throw e;
253
+ }
254
+ const newHandle = await this._sandbox.reconnect(this._commandId, {
255
+ stdoutOffset: this._lastStdoutOffset,
256
+ stderrOffset: this._lastStderrOffset,
257
+ });
258
+ this._stream = newHandle._stream;
259
+ this._control = newHandle._control;
260
+ this._exhausted = false;
261
+ }
262
+ }
263
+ }
264
+ /**
265
+ * Send a kill signal to the running command (SIGKILL).
266
+ *
267
+ * The server kills the entire process group. The stream will
268
+ * subsequently yield an exit message with a non-zero exit code.
269
+ */
270
+ kill() {
271
+ if (this._control) {
272
+ this._control.sendKill();
273
+ }
274
+ }
275
+ /**
276
+ * Write data to the command's stdin.
277
+ */
278
+ sendInput(data) {
279
+ if (this._control) {
280
+ this._control.sendInput(data);
281
+ }
282
+ }
283
+ /** Last known stdout byte offset (for manual reconnection). */
284
+ get lastStdoutOffset() {
285
+ return this._lastStdoutOffset;
286
+ }
287
+ /** Last known stderr byte offset (for manual reconnection). */
288
+ get lastStderrOffset() {
289
+ return this._lastStderrOffset;
290
+ }
291
+ /**
292
+ * Reconnect to this command from the last known offsets.
293
+ *
294
+ * Returns a new CommandHandle that resumes output from where this one
295
+ * left off.
296
+ */
297
+ async reconnect() {
298
+ if (this._commandId === null) {
299
+ throw new errors_js_1.LangSmithSandboxOperationError("Cannot reconnect: command ID not available", "reconnect");
300
+ }
301
+ return this._sandbox.reconnect(this._commandId, {
302
+ stdoutOffset: this._lastStdoutOffset,
303
+ stderrOffset: this._lastStderrOffset,
304
+ });
305
+ }
306
+ }
307
+ exports.CommandHandle = CommandHandle;
308
+ Object.defineProperty(CommandHandle, "MAX_AUTO_RECONNECTS", {
309
+ enumerable: true,
310
+ configurable: true,
311
+ writable: true,
312
+ value: 5
313
+ });
314
+ Object.defineProperty(CommandHandle, "BACKOFF_BASE", {
315
+ enumerable: true,
316
+ configurable: true,
317
+ writable: true,
318
+ value: 0.5
319
+ }); // seconds
320
+ Object.defineProperty(CommandHandle, "BACKOFF_MAX", {
321
+ enumerable: true,
322
+ configurable: true,
323
+ writable: true,
324
+ value: 8.0
325
+ }); // seconds
@@ -0,0 +1,98 @@
1
+ /**
2
+ * CommandHandle - async handle to a running command with streaming output
3
+ * and auto-reconnect.
4
+ *
5
+ * Port of Python's AsyncCommandHandle to TypeScript.
6
+ */
7
+ import type { ExecutionResult, OutputChunk } from "./types.js";
8
+ /**
9
+ * Async handle to a running command with streaming output and auto-reconnect.
10
+ *
11
+ * Async iterable, yielding OutputChunk objects (stdout and stderr interleaved
12
+ * in arrival order). Access .result after iteration to get the full
13
+ * ExecutionResult.
14
+ *
15
+ * Auto-reconnect behavior:
16
+ * - Server hot-reload (1001 Going Away): reconnect immediately
17
+ * - Network error / unexpected close: reconnect with exponential backoff
18
+ * - User called kill(): do NOT reconnect (propagate error)
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const handle = await sandbox.run("make build", { timeout: 600, wait: false });
23
+ *
24
+ * for await (const chunk of handle) { // auto-reconnects on transient errors
25
+ * process.stdout.write(chunk.data);
26
+ * }
27
+ *
28
+ * const result = await handle.result;
29
+ * console.log(`Exit code: ${result.exit_code}`);
30
+ * ```
31
+ */
32
+ export declare class CommandHandle {
33
+ static MAX_AUTO_RECONNECTS: number;
34
+ static BACKOFF_BASE: number;
35
+ static BACKOFF_MAX: number;
36
+ private _stream;
37
+ private _control;
38
+ private _sandbox;
39
+ private _commandId;
40
+ private _pid;
41
+ private _result;
42
+ private _stdoutParts;
43
+ private _stderrParts;
44
+ private _exhausted;
45
+ private _lastStdoutOffset;
46
+ private _lastStderrOffset;
47
+ private _started;
48
+ /**
49
+ * Read the 'started' message to populate commandId and pid.
50
+ *
51
+ * Must be called (and awaited) before iterating for new executions.
52
+ */
53
+ _ensureStarted(): Promise<void>;
54
+ /** The server-assigned command ID. Available after _ensureStarted(). */
55
+ get commandId(): string | null;
56
+ /** The process ID on the sandbox. Available after _ensureStarted(). */
57
+ get pid(): number | null;
58
+ /**
59
+ * The final execution result. Drains the stream if not already exhausted.
60
+ */
61
+ get result(): Promise<ExecutionResult>;
62
+ private _getResult;
63
+ /**
64
+ * Iterate over output chunks from the current stream (no reconnect).
65
+ */
66
+ private _iterStream;
67
+ /**
68
+ * Async iterate over output chunks with auto-reconnect on transient errors.
69
+ *
70
+ * Reconnect strategy:
71
+ * - 1001 Going Away (hot-reload): immediate reconnect, no delay
72
+ * - Other SandboxConnectionError: exponential backoff (0.5s, 1s, 2s...)
73
+ * - After kill(): no reconnect, error propagates
74
+ */
75
+ [Symbol.asyncIterator](): AsyncIterableIterator<OutputChunk>;
76
+ /**
77
+ * Send a kill signal to the running command (SIGKILL).
78
+ *
79
+ * The server kills the entire process group. The stream will
80
+ * subsequently yield an exit message with a non-zero exit code.
81
+ */
82
+ kill(): void;
83
+ /**
84
+ * Write data to the command's stdin.
85
+ */
86
+ sendInput(data: string): void;
87
+ /** Last known stdout byte offset (for manual reconnection). */
88
+ get lastStdoutOffset(): number;
89
+ /** Last known stderr byte offset (for manual reconnection). */
90
+ get lastStderrOffset(): number;
91
+ /**
92
+ * Reconnect to this command from the last known offsets.
93
+ *
94
+ * Returns a new CommandHandle that resumes output from where this one
95
+ * left off.
96
+ */
97
+ reconnect(): Promise<CommandHandle>;
98
+ }