@ai-sdk/sandbox-vercel 1.0.124 → 1.0.126

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/src/utils.ts ADDED
@@ -0,0 +1,313 @@
1
+ import { HarnessSandboxAuthenticationError } from '@ai-sdk/harness';
2
+ import { Sandbox } from '@vercel/sandbox';
3
+
4
+ /**
5
+ * Flattens an intersection of object types into a single object type so the
6
+ * resolved shape displays as its named properties rather than a chain of
7
+ * `A & B & C`.
8
+ */
9
+ export type Prettify<T> = { [K in keyof T]: T[K] } & {};
10
+
11
+ /**
12
+ * Distributes `Omit` across each member of a union instead of collapsing the
13
+ * union to its common keys. `Sandbox.create`'s parameter is a union (a
14
+ * git/tarball/no-source create variant and a snapshot-source create variant),
15
+ * so a plain `Omit` would discard keys absent from any one member (e.g.
16
+ * `runtime`, which the snapshot variant lacks) and merge the `source` shapes.
17
+ * Applying `Omit` per-member preserves every variant intact; the `Prettify`
18
+ * wrapper collapses each member's intersections into a readable object shape.
19
+ */
20
+ export type DistributiveOmit<T, K extends keyof any> = T extends unknown
21
+ ? Prettify<Omit<T, K>>
22
+ : never;
23
+
24
+ /**
25
+ * Parameters forwarded to `@vercel/sandbox`'s `Sandbox.create` when creating
26
+ * a sandbox from scratch. Aliased directly from the underlying SDK so the
27
+ * full surface — every option Vercel supports, including its native
28
+ * `NetworkPolicy` — is available without us re-declaring it.
29
+ */
30
+ export type VercelSandboxCreateParams = DistributiveOmit<
31
+ NonNullable<Parameters<typeof Sandbox.create>[0]>,
32
+ 'onResume'
33
+ >;
34
+
35
+ /**
36
+ * Base shape of `Sandbox.create` params extracted from the union (excludes
37
+ * the `source: { type: 'snapshot' }` variant) so all create-time fields
38
+ * are typed as present.
39
+ */
40
+ export type BaseCreateSandboxParams = Exclude<
41
+ VercelSandboxCreateParams,
42
+ { source: { type: 'snapshot'; snapshotId: string } }
43
+ >;
44
+
45
+ export const VERCEL_PROVIDER_ID = 'vercel-sandbox';
46
+
47
+ /**
48
+ * 30 minutes. The `@vercel/sandbox` SDK defaults to 5 minutes which is
49
+ * too short for multi-step workflows — the VM expires between steps.
50
+ */
51
+ export const DEFAULT_SANDBOX_TIMEOUT_MS = 30 * 60 * 1_000;
52
+ export const DEFAULT_SANDBOX_RUNTIME = 'node24';
53
+
54
+ const SNAPSHOT_POLL_INTERVAL_MS = 500;
55
+ const SNAPSHOT_POLL_TIMEOUT_MS = 30_000;
56
+
57
+ type SandboxLookupParams = {
58
+ fetch?: typeof fetch;
59
+ projectId?: string;
60
+ teamId?: string;
61
+ token?: string;
62
+ };
63
+
64
+ type VercelSandboxAuthenticationSettings = SandboxLookupParams & {
65
+ sandbox?: Sandbox;
66
+ };
67
+
68
+ export function getSandboxLookupParams(
69
+ settings: VercelSandboxAuthenticationSettings,
70
+ ): SandboxLookupParams {
71
+ if ('sandbox' in settings && settings.sandbox != null) {
72
+ return {};
73
+ }
74
+
75
+ const { fetch, projectId, teamId, token } = settings as SandboxLookupParams;
76
+ return {
77
+ ...(fetch ? { fetch } : {}),
78
+ ...(projectId ? { projectId } : {}),
79
+ ...(teamId ? { teamId } : {}),
80
+ ...(token ? { token } : {}),
81
+ };
82
+ }
83
+
84
+ const VERCEL_SANDBOX_AUTHENTICATION_MESSAGE =
85
+ 'Vercel Sandbox authentication failed. Set VERCEL_OIDC_TOKEN, or pass token, teamId, and projectId to createVercelSandbox(), then verify that they can access Vercel Sandbox.';
86
+
87
+ export async function withVercelSandboxAuthenticationError<T>({
88
+ settings,
89
+ operation,
90
+ }: {
91
+ settings: VercelSandboxAuthenticationSettings;
92
+ operation: () => Promise<T>;
93
+ }): Promise<T> {
94
+ try {
95
+ return await operation();
96
+ } catch (error) {
97
+ if (
98
+ !isVercelSandboxAuthenticationFailure({
99
+ error,
100
+ hasConfiguredCredentials: hasConfiguredCredentials(settings),
101
+ })
102
+ ) {
103
+ throw error;
104
+ }
105
+ throw new HarnessSandboxAuthenticationError({
106
+ message: VERCEL_SANDBOX_AUTHENTICATION_MESSAGE,
107
+ sandboxProviderId: VERCEL_PROVIDER_ID,
108
+ cause: error,
109
+ });
110
+ }
111
+ }
112
+
113
+ function hasConfiguredCredentials(
114
+ settings: VercelSandboxAuthenticationSettings,
115
+ ): boolean {
116
+ if (process.env.VERCEL_OIDC_TOKEN) return true;
117
+ if ('sandbox' in settings && settings.sandbox != null) return true;
118
+ const { token, teamId, projectId } = getSandboxLookupParams(settings);
119
+ return Boolean(token && teamId && projectId);
120
+ }
121
+
122
+ function isVercelSandboxAuthenticationFailure({
123
+ error,
124
+ hasConfiguredCredentials,
125
+ }: {
126
+ error: unknown;
127
+ hasConfiguredCredentials: boolean;
128
+ }): boolean {
129
+ const seen = new Set<unknown>();
130
+ let current = error;
131
+ while (current != null && !seen.has(current)) {
132
+ seen.add(current);
133
+ if (typeof current === 'object') {
134
+ const candidate = current as {
135
+ name?: unknown;
136
+ message?: unknown;
137
+ code?: unknown;
138
+ cause?: unknown;
139
+ response?: { status?: unknown; statusCode?: unknown };
140
+ };
141
+ if (
142
+ candidate.response?.status === 401 ||
143
+ candidate.response?.status === 403 ||
144
+ candidate.response?.statusCode === 401 ||
145
+ candidate.response?.statusCode === 403
146
+ ) {
147
+ return true;
148
+ }
149
+ if (
150
+ typeof candidate.name === 'string' &&
151
+ VERCEL_AUTHENTICATION_ERROR_NAMES.has(candidate.name)
152
+ ) {
153
+ return true;
154
+ }
155
+ if (
156
+ typeof candidate.message === 'string' &&
157
+ VERCEL_AUTHENTICATION_ERROR_MESSAGE.test(candidate.message)
158
+ ) {
159
+ return true;
160
+ }
161
+ if (
162
+ !hasConfiguredCredentials &&
163
+ candidate.code === 'ERR_INVALID_ARG_TYPE' &&
164
+ typeof candidate.message === 'string' &&
165
+ candidate.message.includes('"path" argument') &&
166
+ candidate.message.includes('Received undefined')
167
+ ) {
168
+ return true;
169
+ }
170
+ current = candidate.cause;
171
+ continue;
172
+ }
173
+ break;
174
+ }
175
+ return false;
176
+ }
177
+
178
+ const VERCEL_AUTHENTICATION_ERROR_NAMES = new Set([
179
+ 'AccessTokenMissingError',
180
+ 'LocalOidcContextError',
181
+ 'OAuthError',
182
+ 'RefreshAccessTokenFailedError',
183
+ 'VercelOidcContextError',
184
+ 'VercelOidcTokenError',
185
+ ]);
186
+
187
+ const VERCEL_AUTHENTICATION_ERROR_MESSAGE =
188
+ /Could not get credentials from OIDC context|No authentication found|Failed to (?:retrieve|refresh) authentication token|Missing credentials parameters to access the Vercel API|Authentication failed/i;
189
+
190
+ export function hasExplicitSandboxEnvironment(params: object): boolean {
191
+ return (
192
+ ('runtime' in params && params.runtime != null) ||
193
+ ('image' in params && params.image != null) ||
194
+ ('source' in params &&
195
+ typeof params.source === 'object' &&
196
+ params.source != null &&
197
+ 'type' in params.source &&
198
+ params.source.type === 'snapshot')
199
+ );
200
+ }
201
+
202
+ export function withDefaultSandboxSettings(
203
+ createParams: BaseCreateSandboxParams,
204
+ ): BaseCreateSandboxParams {
205
+ return {
206
+ ...(hasExplicitSandboxEnvironment(createParams)
207
+ ? {}
208
+ : { runtime: DEFAULT_SANDBOX_RUNTIME }),
209
+ ...createParams,
210
+ timeout: createParams.timeout ?? DEFAULT_SANDBOX_TIMEOUT_MS,
211
+ } as BaseCreateSandboxParams;
212
+ }
213
+
214
+ export async function ensureTemplateSnapshot({
215
+ baseParams,
216
+ templateName,
217
+ onCreate,
218
+ lookupParams,
219
+ abortSignal,
220
+ snapshotCache,
221
+ }: {
222
+ baseParams: BaseCreateSandboxParams;
223
+ templateName: string;
224
+ onCreate: (sandbox: Sandbox) => Promise<void>;
225
+ lookupParams: SandboxLookupParams;
226
+ abortSignal?: AbortSignal;
227
+ snapshotCache?: Map<string, string>;
228
+ }): Promise<string> {
229
+ const cachedSnapshotId = snapshotCache?.get(templateName);
230
+ if (cachedSnapshotId != null) {
231
+ return cachedSnapshotId;
232
+ }
233
+
234
+ const prepared = await Sandbox.getOrCreate({
235
+ ...baseParams,
236
+ name: templateName,
237
+ persistent: true,
238
+ snapshotExpiration: baseParams.snapshotExpiration ?? 0,
239
+ onCreate,
240
+ ...(abortSignal ? { signal: abortSignal } : {}),
241
+ });
242
+ let snapshotId = prepared.currentSnapshotId;
243
+ if (snapshotId == null) {
244
+ const stopped = await prepared.stop(
245
+ abortSignal ? { signal: abortSignal } : undefined,
246
+ );
247
+ snapshotId =
248
+ stopped.snapshot?.id ??
249
+ (await pollForTemplateSnapshot({
250
+ name: templateName,
251
+ lookupParams,
252
+ abortSignal,
253
+ }));
254
+ }
255
+ snapshotCache?.set(templateName, snapshotId);
256
+ return snapshotId;
257
+ }
258
+
259
+ export function createLiveSandboxFromSnapshot({
260
+ baseParams,
261
+ snapshotId,
262
+ liveName,
263
+ abortSignal,
264
+ }: {
265
+ baseParams: BaseCreateSandboxParams;
266
+ snapshotId: string;
267
+ liveName?: string;
268
+ abortSignal?: AbortSignal;
269
+ }): Promise<Sandbox> {
270
+ const {
271
+ runtime: _runtime,
272
+ image: _image,
273
+ source: _source,
274
+ persistent: _persistent,
275
+ ...forkParams
276
+ } = baseParams;
277
+ return Sandbox.create({
278
+ ...forkParams,
279
+ source: { type: 'snapshot', snapshotId },
280
+ ...(liveName != null ? { name: liveName } : {}),
281
+ ...(abortSignal ? { signal: abortSignal } : {}),
282
+ });
283
+ }
284
+
285
+ export async function pollForTemplateSnapshot({
286
+ name,
287
+ lookupParams,
288
+ abortSignal,
289
+ }: {
290
+ name: string;
291
+ lookupParams: SandboxLookupParams;
292
+ abortSignal: AbortSignal | undefined;
293
+ }): Promise<string> {
294
+ const deadline = Date.now() + SNAPSHOT_POLL_TIMEOUT_MS;
295
+ while (Date.now() < deadline) {
296
+ abortSignal?.throwIfAborted();
297
+ const refreshed = await Sandbox.get({
298
+ ...lookupParams,
299
+ name,
300
+ resume: false,
301
+ ...(abortSignal ? { signal: abortSignal } : {}),
302
+ });
303
+ if (refreshed.currentSnapshotId) {
304
+ return refreshed.currentSnapshotId;
305
+ }
306
+ await new Promise<void>(resolve =>
307
+ setTimeout(resolve, SNAPSHOT_POLL_INTERVAL_MS),
308
+ );
309
+ }
310
+ throw new Error(
311
+ `Timed out waiting for snapshot of template "${name}" to publish.`,
312
+ );
313
+ }
@@ -0,0 +1,221 @@
1
+ import type {
2
+ HarnessV1NetworkSandboxSession,
3
+ HarnessV1SandboxProvider,
4
+ } from '@ai-sdk/harness';
5
+ import type { Experimental_SandboxSession as SandboxSession } from '@ai-sdk/provider-utils';
6
+ import { Sandbox } from '@vercel/sandbox';
7
+ import { VercelNetworkSandboxSession } from './vercel-network-sandbox-session';
8
+ import { VercelSandboxSession } from './vercel-sandbox-session';
9
+ import {
10
+ VERCEL_PROVIDER_ID,
11
+ createLiveSandboxFromSnapshot,
12
+ ensureTemplateSnapshot,
13
+ getSandboxLookupParams,
14
+ withVercelSandboxAuthenticationError,
15
+ withDefaultSandboxSettings,
16
+ type BaseCreateSandboxParams,
17
+ } from './utils';
18
+
19
+ type VercelSandboxCreateParams = NonNullable<
20
+ Parameters<typeof Sandbox.create>[0]
21
+ >;
22
+
23
+ type DistributiveOmit<T, K extends keyof any> = T extends unknown
24
+ ? Omit<T, K>
25
+ : never;
26
+
27
+ type VercelSandboxCreationSettings = DistributiveOmit<
28
+ VercelSandboxCreateParams,
29
+ 'onResume'
30
+ > & {
31
+ sandbox?: never;
32
+ name?: string;
33
+ };
34
+
35
+ /**
36
+ * Settings for `createVercelSandbox`. Two mutually-exclusive shapes:
37
+ *
38
+ * - `{ sandbox }` — wrap an already-created `@vercel/sandbox` `Sandbox`. The
39
+ * caller owns its lifecycle; the provider's `stop()` and `destroy()` are
40
+ * no-ops.
41
+ * - {@link VercelSandboxCreateParams} fields — provider creates the underlying
42
+ * sandbox. When the adapter declares a bootstrap recipe the provider uses
43
+ * `Sandbox.getOrCreate` to maintain a persistent named template snapshot
44
+ * keyed by the recipe identity, and forks an ephemeral sandbox per session
45
+ * from the snapshot. Use `name` to override the auto-derived template name.
46
+ *
47
+ * @deprecated Use `VercelNetworkSandboxSessionCreateOptions` for new sessions.
48
+ */
49
+ export type VercelSandboxSettings =
50
+ | { sandbox: Sandbox }
51
+ | VercelSandboxCreationSettings;
52
+
53
+ const SESSION_NAME_PREFIX = 'ai-sdk-harness-session';
54
+
55
+ function sessionSandboxName(sessionId: string): string {
56
+ return `${SESSION_NAME_PREFIX}-${sessionId}`;
57
+ }
58
+
59
+ /** @deprecated Use `createVercelNetworkSandboxSession` instead. */
60
+ export function createVercelSandbox(
61
+ settings: VercelSandboxSettings = {} as VercelSandboxSettings,
62
+ ): HarnessV1SandboxProvider {
63
+ console.warn(
64
+ 'createVercelSandbox is deprecated. Use createVercelNetworkSandboxSession and HarnessAgent.createSession({ sandboxSession }) instead.',
65
+ );
66
+ return new VercelSandboxProvider(settings);
67
+ }
68
+
69
+ /**
70
+ * `HarnessV1SandboxProvider` implementation backed by `@vercel/sandbox`.
71
+ * Construct one via {@link createVercelSandbox} at module scope and pass it
72
+ * to a `HarnessAgent` (or call `createSession()` directly if you want raw
73
+ * access to a network sandbox session).
74
+ */
75
+ /** @deprecated Use `createVercelNetworkSandboxSession` instead. */
76
+ export class VercelSandboxProvider implements HarnessV1SandboxProvider {
77
+ readonly specificationVersion = 'harness-sandbox-v1' as const;
78
+ readonly providerId = VERCEL_PROVIDER_ID;
79
+
80
+ constructor(private readonly settings: VercelSandboxSettings) {
81
+ console.warn(
82
+ 'VercelSandboxProvider is deprecated. Use createVercelNetworkSandboxSession instead.',
83
+ );
84
+ }
85
+
86
+ createSession = async (options?: {
87
+ sessionId?: string;
88
+ abortSignal?: AbortSignal;
89
+ identity?: string;
90
+ onFirstCreate?: (
91
+ session: SandboxSession,
92
+ opts: { abortSignal?: AbortSignal },
93
+ ) => Promise<void>;
94
+ }): Promise<HarnessV1NetworkSandboxSession> => {
95
+ options?.abortSignal?.throwIfAborted();
96
+
97
+ if ('sandbox' in this.settings && this.settings.sandbox != null) {
98
+ return new VercelNetworkSandboxSession({
99
+ sandbox: this.settings.sandbox,
100
+ ownsLifecycle: false,
101
+ });
102
+ }
103
+
104
+ type CreateNewBranch = BaseCreateSandboxParams & {
105
+ sandbox?: never;
106
+ name?: string;
107
+ };
108
+ const settings = this.settings as CreateNewBranch;
109
+ const {
110
+ sandbox: _ignoredSandbox,
111
+ name: explicitName,
112
+ ...createParams
113
+ } = settings;
114
+ // Sandbox v3 changed its implicit default from the Node 24 runtime to the
115
+ // Universal managed image. Keep this adapter's existing default stable while
116
+ // allowing callers to opt into managed images explicitly.
117
+ const baseParams = withDefaultSandboxSettings(
118
+ createParams as BaseCreateSandboxParams,
119
+ );
120
+
121
+ const identity = options?.identity;
122
+ const onFirstCreate = options?.onFirstCreate;
123
+
124
+ // When sessionId is supplied, name the per-session sandbox deterministically
125
+ // so a future `resumeSession({ sessionId })` can locate it via
126
+ // `Sandbox.get({ name })`. Absent sessionId (e.g. prewarm), fall back to
127
+ // Vercel's auto-naming.
128
+ const sessionNameOverride = options?.sessionId
129
+ ? { name: sessionSandboxName(options.sessionId) }
130
+ : {};
131
+
132
+ if (identity == null || onFirstCreate == null) {
133
+ const sandbox = await withVercelSandboxAuthenticationError({
134
+ settings: this.settings,
135
+ operation: () =>
136
+ Sandbox.create({
137
+ ...baseParams,
138
+ ...sessionNameOverride,
139
+ ...(options?.abortSignal ? { signal: options.abortSignal } : {}),
140
+ }),
141
+ });
142
+ return new VercelNetworkSandboxSession({ sandbox, ownsLifecycle: true });
143
+ }
144
+
145
+ const templateName = explicitName ?? `ai-sdk-harness-${identity}`;
146
+ const snapshotId = await withVercelSandboxAuthenticationError({
147
+ settings: this.settings,
148
+ operation: () =>
149
+ ensureTemplateSnapshot({
150
+ baseParams,
151
+ templateName,
152
+ lookupParams: getSandboxLookupParams(baseParams),
153
+ abortSignal: options?.abortSignal,
154
+ snapshotCache: getSnapshotCache(),
155
+ onCreate: async sbx => {
156
+ await onFirstCreate(new VercelSandboxSession(sbx), {
157
+ abortSignal: options?.abortSignal,
158
+ });
159
+ },
160
+ }),
161
+ });
162
+
163
+ const fork = await withVercelSandboxAuthenticationError({
164
+ settings: this.settings,
165
+ operation: () =>
166
+ createLiveSandboxFromSnapshot({
167
+ baseParams,
168
+ snapshotId,
169
+ liveName: sessionNameOverride.name,
170
+ abortSignal: options?.abortSignal,
171
+ }),
172
+ });
173
+ return new VercelNetworkSandboxSession({
174
+ sandbox: fork,
175
+ ownsLifecycle: true,
176
+ });
177
+ };
178
+
179
+ resumeSession = async (options: {
180
+ sessionId: string;
181
+ abortSignal?: AbortSignal;
182
+ }): Promise<HarnessV1NetworkSandboxSession> => {
183
+ options.abortSignal?.throwIfAborted();
184
+
185
+ // Wrap-existing case: caller owns the sandbox. Same session as createSession.
186
+ if ('sandbox' in this.settings && this.settings.sandbox != null) {
187
+ return new VercelNetworkSandboxSession({
188
+ sandbox: this.settings.sandbox,
189
+ ownsLifecycle: false,
190
+ });
191
+ }
192
+
193
+ const sandbox = await withVercelSandboxAuthenticationError({
194
+ settings: this.settings,
195
+ operation: () =>
196
+ Sandbox.get({
197
+ ...getSandboxLookupParams(this.settings),
198
+ name: sessionSandboxName(options.sessionId),
199
+ ...(options.abortSignal ? { signal: options.abortSignal } : {}),
200
+ }),
201
+ });
202
+ return new VercelNetworkSandboxSession({ sandbox, ownsLifecycle: true });
203
+ };
204
+ }
205
+
206
+ const SNAPSHOT_CACHE_KEY = Symbol.for(
207
+ 'ai-sdk.harness.vercel-template-snapshots',
208
+ );
209
+
210
+ type SnapshotCache = Map<string, string>;
211
+ function getSnapshotCache(): SnapshotCache {
212
+ const globals = globalThis as {
213
+ [SNAPSHOT_CACHE_KEY]?: SnapshotCache;
214
+ };
215
+ let cache = globals[SNAPSHOT_CACHE_KEY];
216
+ if (cache == null) {
217
+ cache = new Map();
218
+ globals[SNAPSHOT_CACHE_KEY] = cache;
219
+ }
220
+ return cache;
221
+ }
@@ -14,11 +14,11 @@ const VERCEL_PROVIDER_ID = 'vercel-sandbox';
14
14
 
15
15
  /**
16
16
  * `HarnessV1NetworkSandboxSession` backed by a `@vercel/sandbox` `Sandbox`. The
17
- * provider's `create()` returns one of these. It extends
18
- * {@link VercelSandboxSession} with the infra surface (ports, lifecycle,
19
- * network policy). It owns the sandbox's lifecycle only when the provider
20
- * created it; when the provider was given an existing sandbox, `stop()` and
21
- * `destroy()` are no-ops (caller retains ownership).
17
+ * native adaptation and sandbox creation functions return one of these. It
18
+ * extends {@link VercelSandboxSession} with ports, lifecycle, and network
19
+ * policy. Explicit `stop()` and `destroy()` calls delegate to the native
20
+ * sandbox unless the session was wrapped through the compatibility provider's
21
+ * existing-sandbox path, where those calls are intentionally no-ops.
22
22
  */
23
23
  export class VercelNetworkSandboxSession
24
24
  extends VercelSandboxSession
@@ -1,8 +1,8 @@
1
1
  import { posix } from 'node:path';
2
2
  import {
3
3
  extractLines,
4
- type Experimental_SandboxSession,
5
- type Experimental_SandboxProcess,
4
+ type Experimental_SandboxSession as SandboxSession,
5
+ type Experimental_SandboxProcess as SandboxProcess,
6
6
  } from '@ai-sdk/provider-utils';
7
7
  import type { Sandbox, Command } from '@vercel/sandbox';
8
8
 
@@ -13,7 +13,7 @@ import type { Sandbox, Command } from '@vercel/sandbox';
13
13
  * constructed directly by consumers. The network sandbox session owns the
14
14
  * lifetime of the underlying sandbox.
15
15
  */
16
- export class VercelSandboxSession implements Experimental_SandboxSession {
16
+ export class VercelSandboxSession implements SandboxSession {
17
17
  constructor(protected readonly sandbox: Sandbox) {}
18
18
 
19
19
  get description(): string {
@@ -66,7 +66,7 @@ export class VercelSandboxSession implements Experimental_SandboxSession {
66
66
  workingDirectory?: string;
67
67
  env?: Record<string, string>;
68
68
  abortSignal?: AbortSignal;
69
- }): Promise<Experimental_SandboxProcess> {
69
+ }): Promise<SandboxProcess> {
70
70
  abortSignal?.throwIfAborted();
71
71
 
72
72
  const live = await this.sandbox.runCommand({
@@ -201,7 +201,7 @@ export class VercelSandboxSession implements Experimental_SandboxSession {
201
201
  function createSandboxProcess(
202
202
  command: Command,
203
203
  abortSignal: AbortSignal | undefined,
204
- ): Experimental_SandboxProcess {
204
+ ): SandboxProcess {
205
205
  const encoder = new TextEncoder();
206
206
  const controllers: {
207
207
  stdout?: ReadableStreamDefaultController<Uint8Array>;