zeitlich 0.2.41 → 0.2.42

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 (119) hide show
  1. package/dist/{activities-qUflxmfS.d.cts → activities-Coafq5zr.d.cts} +2 -2
  2. package/dist/{activities-D_g13S3y.d.ts → activities-CrN-ghLo.d.ts} +2 -2
  3. package/dist/adapters/sandbox/daytona/index.cjs +4 -23
  4. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  5. package/dist/adapters/sandbox/daytona/index.d.cts +18 -86
  6. package/dist/adapters/sandbox/daytona/index.d.ts +18 -86
  7. package/dist/adapters/sandbox/daytona/index.js +4 -23
  8. package/dist/adapters/sandbox/daytona/index.js.map +1 -1
  9. package/dist/adapters/sandbox/daytona/workflow.cjs +1 -7
  10. package/dist/adapters/sandbox/daytona/workflow.cjs.map +1 -1
  11. package/dist/adapters/sandbox/daytona/workflow.d.cts +9 -2
  12. package/dist/adapters/sandbox/daytona/workflow.d.ts +9 -2
  13. package/dist/adapters/sandbox/daytona/workflow.js +1 -7
  14. package/dist/adapters/sandbox/daytona/workflow.js.map +1 -1
  15. package/dist/adapters/sandbox/e2b/index.cjs +9 -0
  16. package/dist/adapters/sandbox/e2b/index.cjs.map +1 -1
  17. package/dist/adapters/sandbox/e2b/index.d.cts +13 -5
  18. package/dist/adapters/sandbox/e2b/index.d.ts +13 -5
  19. package/dist/adapters/sandbox/e2b/index.js +9 -1
  20. package/dist/adapters/sandbox/e2b/index.js.map +1 -1
  21. package/dist/adapters/sandbox/e2b/workflow.cjs.map +1 -1
  22. package/dist/adapters/sandbox/e2b/workflow.d.cts +4 -2
  23. package/dist/adapters/sandbox/e2b/workflow.d.ts +4 -2
  24. package/dist/adapters/sandbox/e2b/workflow.js.map +1 -1
  25. package/dist/adapters/sandbox/inmemory/index.cjs +11 -0
  26. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
  27. package/dist/adapters/sandbox/inmemory/index.d.cts +11 -3
  28. package/dist/adapters/sandbox/inmemory/index.d.ts +11 -3
  29. package/dist/adapters/sandbox/inmemory/index.js +11 -1
  30. package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
  31. package/dist/adapters/sandbox/inmemory/workflow.cjs.map +1 -1
  32. package/dist/adapters/sandbox/inmemory/workflow.d.cts +4 -2
  33. package/dist/adapters/sandbox/inmemory/workflow.d.ts +4 -2
  34. package/dist/adapters/sandbox/inmemory/workflow.js.map +1 -1
  35. package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
  36. package/dist/adapters/thread/anthropic/index.d.cts +5 -5
  37. package/dist/adapters/thread/anthropic/index.d.ts +5 -5
  38. package/dist/adapters/thread/anthropic/index.js.map +1 -1
  39. package/dist/adapters/thread/anthropic/workflow.d.cts +5 -5
  40. package/dist/adapters/thread/anthropic/workflow.d.ts +5 -5
  41. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  42. package/dist/adapters/thread/google-genai/index.d.cts +5 -5
  43. package/dist/adapters/thread/google-genai/index.d.ts +5 -5
  44. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  45. package/dist/adapters/thread/google-genai/workflow.d.cts +5 -5
  46. package/dist/adapters/thread/google-genai/workflow.d.ts +5 -5
  47. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  48. package/dist/adapters/thread/langchain/index.d.cts +5 -5
  49. package/dist/adapters/thread/langchain/index.d.ts +5 -5
  50. package/dist/adapters/thread/langchain/index.js.map +1 -1
  51. package/dist/adapters/thread/langchain/workflow.d.cts +5 -5
  52. package/dist/adapters/thread/langchain/workflow.d.ts +5 -5
  53. package/dist/index.cjs +224 -70
  54. package/dist/index.cjs.map +1 -1
  55. package/dist/index.d.cts +66 -16
  56. package/dist/index.d.ts +66 -16
  57. package/dist/index.js +224 -70
  58. package/dist/index.js.map +1 -1
  59. package/dist/{proxy-D7mvDEO6.d.cts → proxy-Bf7uI-Hw.d.cts} +1 -1
  60. package/dist/{proxy-BbcgoXg1.d.ts → proxy-COqA95FW.d.ts} +1 -1
  61. package/dist/{thread-manager-CTXPCu9W.d.ts → thread-manager-BhkOyQ1I.d.ts} +2 -2
  62. package/dist/{thread-manager-Dqstsw4i.d.ts → thread-manager-Bi1XlbpJ.d.ts} +2 -2
  63. package/dist/{thread-manager-cLhDhRRc.d.cts → thread-manager-BsLO3Fgc.d.cts} +2 -2
  64. package/dist/{thread-manager-DrWfVjlj.d.cts → thread-manager-wRVVBFgj.d.cts} +2 -2
  65. package/dist/{types-CjF1_Idx.d.ts → types-BkX4HLzi.d.ts} +1 -1
  66. package/dist/{types-CdvcmXb6.d.cts → types-C66-BVBr.d.cts} +1 -1
  67. package/dist/types-CJ7tCdl6.d.cts +266 -0
  68. package/dist/types-CJ7tCdl6.d.ts +266 -0
  69. package/dist/{types-DjaQKUJx.d.cts → types-CdALEF3z.d.cts} +300 -20
  70. package/dist/{types-BqTmyH31.d.ts → types-ChAy_jSP.d.ts} +300 -20
  71. package/dist/types-CjY93AWZ.d.cts +84 -0
  72. package/dist/types-gVa5XCWD.d.ts +84 -0
  73. package/dist/{workflow-N1MNDoul.d.ts → workflow-BwT5EybR.d.ts} +7 -6
  74. package/dist/{workflow-CuqxgS6X.d.cts → workflow-DMmiaw6w.d.cts} +7 -6
  75. package/dist/workflow.cjs +99 -46
  76. package/dist/workflow.cjs.map +1 -1
  77. package/dist/workflow.d.cts +4 -4
  78. package/dist/workflow.d.ts +4 -4
  79. package/dist/workflow.js +99 -46
  80. package/dist/workflow.js.map +1 -1
  81. package/package.json +7 -32
  82. package/src/adapters/sandbox/daytona/index.ts +25 -48
  83. package/src/adapters/sandbox/daytona/proxy.ts +7 -8
  84. package/src/adapters/sandbox/e2b/index.ts +21 -6
  85. package/src/adapters/sandbox/e2b/proxy.ts +3 -2
  86. package/src/adapters/sandbox/inmemory/index.ts +21 -1
  87. package/src/adapters/sandbox/inmemory/proxy.ts +7 -3
  88. package/src/lib/activity.ts +5 -0
  89. package/src/lib/sandbox/capability-types.test.ts +859 -0
  90. package/src/lib/sandbox/index.ts +1 -0
  91. package/src/lib/sandbox/manager.ts +187 -31
  92. package/src/lib/sandbox/types.ts +189 -46
  93. package/src/lib/session/index.ts +1 -0
  94. package/src/lib/session/session.ts +97 -35
  95. package/src/lib/session/types.ts +181 -5
  96. package/src/lib/subagent/handler.ts +66 -43
  97. package/src/lib/subagent/types.ts +484 -16
  98. package/src/workflow.ts +3 -0
  99. package/tsup.config.ts +0 -4
  100. package/dist/adapters/sandbox/bedrock/index.cjs +0 -457
  101. package/dist/adapters/sandbox/bedrock/index.cjs.map +0 -1
  102. package/dist/adapters/sandbox/bedrock/index.d.cts +0 -25
  103. package/dist/adapters/sandbox/bedrock/index.d.ts +0 -25
  104. package/dist/adapters/sandbox/bedrock/index.js +0 -454
  105. package/dist/adapters/sandbox/bedrock/index.js.map +0 -1
  106. package/dist/adapters/sandbox/bedrock/workflow.cjs +0 -36
  107. package/dist/adapters/sandbox/bedrock/workflow.cjs.map +0 -1
  108. package/dist/adapters/sandbox/bedrock/workflow.d.cts +0 -29
  109. package/dist/adapters/sandbox/bedrock/workflow.d.ts +0 -29
  110. package/dist/adapters/sandbox/bedrock/workflow.js +0 -34
  111. package/dist/adapters/sandbox/bedrock/workflow.js.map +0 -1
  112. package/dist/types-DAsQ21Rt.d.ts +0 -74
  113. package/dist/types-lm8tMNJQ.d.cts +0 -74
  114. package/dist/types-yx0LzPGn.d.cts +0 -173
  115. package/dist/types-yx0LzPGn.d.ts +0 -173
  116. package/src/adapters/sandbox/bedrock/filesystem.ts +0 -340
  117. package/src/adapters/sandbox/bedrock/index.ts +0 -274
  118. package/src/adapters/sandbox/bedrock/proxy.ts +0 -59
  119. package/src/adapters/sandbox/bedrock/types.ts +0 -24
@@ -4,6 +4,7 @@ export { toTree } from "./tree";
4
4
  export type {
5
5
  Sandbox,
6
6
  SandboxCapabilities,
7
+ SandboxCapability,
7
8
  SandboxCreateOptions,
8
9
  SandboxFileSystem,
9
10
  SandboxOps,
@@ -1,11 +1,34 @@
1
1
  import type {
2
2
  Sandbox,
3
+ SandboxCapability,
3
4
  SandboxCreateOptions,
4
5
  SandboxOps,
5
6
  PrefixedSandboxOps,
6
7
  SandboxProvider,
7
8
  SandboxSnapshot,
8
9
  } from "./types";
10
+ import { SandboxNotSupportedError } from "./types";
11
+
12
+ /**
13
+ * Method names the manager treats as capability-gated, mirroring the
14
+ * conditional fields on {@link SandboxProvider}. The full list is used
15
+ * by the constructor-time consistency check below to assert that, for
16
+ * each gated method present on the provider at runtime, the matching
17
+ * capability is also declared in `supportedCapabilities` (and vice
18
+ * versa). This is the runtime half of the type↔runtime alignment guard
19
+ * the type-level constraint can't enforce on its own.
20
+ */
21
+ const CAP_METHOD_TO_CAPABILITY: ReadonlyArray<{
22
+ method: string;
23
+ capability: SandboxCapability;
24
+ }> = [
25
+ { method: "pause", capability: "pause" },
26
+ { method: "resume", capability: "resume" },
27
+ { method: "snapshot", capability: "snapshot" },
28
+ { method: "deleteSnapshot", capability: "snapshot" },
29
+ { method: "restore", capability: "restore" },
30
+ { method: "fork", capability: "fork" },
31
+ ];
9
32
 
10
33
  /**
11
34
  * Result returned by {@link SandboxManagerHooks.onPreCreate}.
@@ -61,8 +84,16 @@ export interface SandboxManagerHooks<
61
84
  /**
62
85
  * Stateless facade over a {@link SandboxProvider}.
63
86
  *
64
- * Delegates all lifecycle operations to the provider, which is responsible
65
- * for its own instance management strategy (e.g. in-memory map, remote API).
87
+ * Generic over the same capability set (`TCaps`) as the underlying
88
+ * provider. The manager's lifecycle methods are always present on the
89
+ * class (so existing call sites compile unchanged), but
90
+ * {@link SandboxManager.createActivities} is capability-gated: only
91
+ * activities whose capability the provider declares via
92
+ * {@link SandboxProvider.supportedCapabilities} are wrapped, and the
93
+ * returned object's type omits absent ones.
94
+ *
95
+ * The default `TCaps = SandboxCapability` keeps the full method surface
96
+ * for existing usages that only pass `TOptions` / `TSandbox` / `TId`.
66
97
  *
67
98
  * Optional {@link SandboxManagerHooks} can be passed at construction time.
68
99
  * The `onPreCreate` hook runs inside the `createSandbox` activity, receiving
@@ -105,16 +136,62 @@ export class SandboxManager<
105
136
  TSandbox extends Sandbox = Sandbox,
106
137
  TId extends string = string,
107
138
  TCtx = unknown,
139
+ TCaps extends SandboxCapability = SandboxCapability,
108
140
  > {
109
141
  private hooks: SandboxManagerHooks<TOptions, TCtx>;
110
142
 
111
143
  constructor(
112
- private provider: SandboxProvider<TOptions, TSandbox> & {
144
+ private provider: SandboxProvider<TOptions, TSandbox, TCaps> & {
113
145
  readonly id: TId;
114
146
  },
115
147
  options?: { hooks?: SandboxManagerHooks<TOptions, TCtx> }
116
148
  ) {
117
149
  this.hooks = options?.hooks ?? {};
150
+ this.assertCapabilityRuntimeConsistency();
151
+ }
152
+
153
+ /**
154
+ * Verifies that the provider's runtime `supportedCapabilities` set is
155
+ * consistent with the gated methods physically present on the provider.
156
+ *
157
+ * Belt-and-suspenders complement to the type-level
158
+ * `ReadonlySet<TCaps & SandboxCapability>` constraint: TypeScript can
159
+ * prevent the runtime set from containing capabilities not declared in
160
+ * `TCaps`, but it cannot detect a provider that **declares** a cap in
161
+ * `TCaps` and forgets to include it in the runtime set (or that ships
162
+ * a method without listing its cap). Both shapes silently break
163
+ * activity registration, so we trip a loud failure at construction
164
+ * time instead.
165
+ *
166
+ * Adapters that derive both surfaces from a single `as const`
167
+ * capability array (the recommended pattern) pass this check by
168
+ * construction.
169
+ */
170
+ private assertCapabilityRuntimeConsistency(): void {
171
+ const supported = this.provider
172
+ .supportedCapabilities as ReadonlySet<SandboxCapability>;
173
+ for (const { method, capability } of CAP_METHOD_TO_CAPABILITY) {
174
+ const hasMethod =
175
+ typeof (this.provider as unknown as Record<string, unknown>)[method] ===
176
+ "function";
177
+ const declaresCap = supported.has(capability);
178
+ if (hasMethod && !declaresCap) {
179
+ throw new Error(
180
+ `Sandbox provider "${this.provider.id}" implements ${method}() but ` +
181
+ `does not list "${capability}" in supportedCapabilities. ` +
182
+ `Add the capability to the provider's runtime set so activities ` +
183
+ `for it can be registered.`
184
+ );
185
+ }
186
+ if (declaresCap && !hasMethod) {
187
+ throw new Error(
188
+ `Sandbox provider "${this.provider.id}" lists "${capability}" in ` +
189
+ `supportedCapabilities but does not implement ${method}(). ` +
190
+ `Either add the method to the provider or remove the capability ` +
191
+ `from supportedCapabilities.`
192
+ );
193
+ }
194
+ }
118
195
  }
119
196
 
120
197
  async create(
@@ -165,35 +242,84 @@ export class SandboxManager<
165
242
  await this.provider.destroy(id);
166
243
  }
167
244
 
245
+ /**
246
+ * Capability-gated lifecycle methods on the underlying provider.
247
+ *
248
+ * These manager methods always exist at runtime; calling one whose
249
+ * capability is absent from the provider's `supportedCapabilities`
250
+ * throws an error. The activities returned from
251
+ * {@link SandboxManager.createActivities} are gated at the type level
252
+ * via `TCaps`, which is where compile-time safety is enforced.
253
+ */
168
254
  async pause(id: string, ttlSeconds?: number): Promise<void> {
169
- await this.provider.pause(id, ttlSeconds);
255
+ const fn = this.providerMethod("pause") as
256
+ | ((id: string, ttlSeconds?: number) => Promise<void>)
257
+ | undefined;
258
+ if (!fn) throw this.unsupported("pause");
259
+ await fn.call(this.provider, id, ttlSeconds);
170
260
  }
171
261
 
172
262
  async resume(id: string): Promise<void> {
173
- await this.provider.resume(id);
263
+ const fn = this.providerMethod("resume") as
264
+ | ((id: string) => Promise<void>)
265
+ | undefined;
266
+ if (!fn) throw this.unsupported("resume");
267
+ await fn.call(this.provider, id);
174
268
  }
175
269
 
176
270
  async snapshot(id: string, options?: TOptions): Promise<SandboxSnapshot> {
177
- return this.provider.snapshot(id, options);
271
+ const fn = this.providerMethod("snapshot") as
272
+ | ((id: string, options?: TOptions) => Promise<SandboxSnapshot>)
273
+ | undefined;
274
+ if (!fn) throw this.unsupported("snapshot");
275
+ return fn.call(this.provider, id, options);
178
276
  }
179
277
 
180
278
  async restore(
181
279
  snapshot: SandboxSnapshot,
182
280
  options?: TOptions
183
281
  ): Promise<string> {
184
- const sandbox = await this.provider.restore(snapshot, options);
282
+ const fn = this.providerMethod("restore") as
283
+ | ((snap: SandboxSnapshot, options?: TOptions) => Promise<TSandbox>)
284
+ | undefined;
285
+ if (!fn) throw this.unsupported("restore");
286
+ const sandbox = await fn.call(this.provider, snapshot, options);
185
287
  return sandbox.id;
186
288
  }
187
289
 
188
290
  async deleteSnapshot(snapshot: SandboxSnapshot): Promise<void> {
189
- await this.provider.deleteSnapshot(snapshot);
291
+ const fn = this.providerMethod("deleteSnapshot") as
292
+ | ((snap: SandboxSnapshot) => Promise<void>)
293
+ | undefined;
294
+ if (!fn) throw this.unsupported("deleteSnapshot");
295
+ await fn.call(this.provider, snapshot);
190
296
  }
191
297
 
192
298
  async fork(sandboxId: string, options?: TOptions): Promise<string> {
193
- const sandbox = await this.provider.fork(sandboxId, options);
299
+ const fn = this.providerMethod("fork") as
300
+ | ((id: string, options?: TOptions) => Promise<TSandbox>)
301
+ | undefined;
302
+ if (!fn) throw this.unsupported("fork");
303
+ const sandbox = await fn.call(this.provider, sandboxId, options);
194
304
  return sandbox.id;
195
305
  }
196
306
 
307
+ private providerMethod(name: string): unknown {
308
+ const value = (this.provider as unknown as Record<string, unknown>)[name];
309
+ return typeof value === "function" ? value : undefined;
310
+ }
311
+
312
+ /**
313
+ * Constructs the structured error thrown when an unsupported lifecycle
314
+ * method is invoked through the manager. Uses the public
315
+ * {@link SandboxNotSupportedError} symbol so consumers that catch on
316
+ * `instanceof SandboxNotSupportedError` (the documented compatibility
317
+ * path) keep matching after the refactor.
318
+ */
319
+ private unsupported(name: string): SandboxNotSupportedError {
320
+ return new SandboxNotSupportedError(name);
321
+ }
322
+
197
323
  /**
198
324
  * Returns Temporal activity functions with prefixed names.
199
325
  *
@@ -201,6 +327,11 @@ export class SandboxManager<
201
327
  * to pass the workflow/scope name. Use the matching `proxy*SandboxOps()`
202
328
  * helper from the adapter's `/workflow` entrypoint on the workflow side.
203
329
  *
330
+ * Activities are only registered for capabilities the provider declares
331
+ * via {@link SandboxProvider.supportedCapabilities}: methods omitted
332
+ * from the cap set are not wrapped, and the returned object's type
333
+ * omits the corresponding keys.
334
+ *
204
335
  * @param scope - Workflow name (appended to the provider id)
205
336
  *
206
337
  * @example
@@ -211,14 +342,25 @@ export class SandboxManager<
211
342
  *
212
343
  * const dmgr = new SandboxManager(new DaytonaSandboxProvider(config));
213
344
  * dmgr.createActivities("CodingAgent");
214
- * // registers: daytonaCodingAgentCreateSandbox,
345
+ * // registers: daytonaCodingAgentCreateSandbox, daytonaCodingAgentDestroySandbox
346
+ * // (snapshot/restore/fork/pause/resume omitted — Daytona doesn't declare them)
215
347
  * ```
216
348
  */
217
349
  createActivities<S extends string>(
218
350
  scope: S
219
- ): PrefixedSandboxOps<`${TId}${Capitalize<S>}`, TOptions, TCtx> {
351
+ ): PrefixedSandboxOps<`${TId}${Capitalize<S>}`, TOptions, TCtx, TCaps> {
220
352
  const prefix = `${this.provider.id}${scope.charAt(0).toUpperCase()}${scope.slice(1)}`;
221
- const ops: SandboxOps<TOptions, TCtx> = {
353
+ const cap = (s: string): string => s.charAt(0).toUpperCase() + s.slice(1);
354
+ // The set is statically typed against the (possibly narrow) `TCaps`,
355
+ // but we need to probe every well-known capability here. Widen for
356
+ // the duration of the lookup; the constructor-time consistency check
357
+ // already ensures these probes can't accidentally observe a method
358
+ // present on the provider that's missing from the declared set.
359
+ const supported = this.provider
360
+ .supportedCapabilities as ReadonlySet<SandboxCapability>;
361
+
362
+ type WideOps = SandboxOps<TOptions, TCtx>;
363
+ const ops: Partial<WideOps> = {
222
364
  createSandbox: async (
223
365
  options?: TOptions,
224
366
  ctx?: TCtx
@@ -228,42 +370,56 @@ export class SandboxManager<
228
370
  destroySandbox: async (sandboxId: string): Promise<void> => {
229
371
  await this.destroy(sandboxId);
230
372
  },
231
- pauseSandbox: async (
373
+ };
374
+
375
+ if (supported.has("pause")) {
376
+ ops.pauseSandbox = async (
232
377
  sandboxId: string,
233
378
  ttlSeconds?: number
234
379
  ): Promise<void> => {
235
380
  await this.pause(sandboxId, ttlSeconds);
236
- },
237
- resumeSandbox: async (sandboxId: string): Promise<void> => {
381
+ };
382
+ }
383
+ if (supported.has("resume")) {
384
+ ops.resumeSandbox = async (sandboxId: string): Promise<void> => {
238
385
  await this.resume(sandboxId);
239
- },
240
- snapshotSandbox: async (
386
+ };
387
+ }
388
+ if (supported.has("snapshot")) {
389
+ ops.snapshotSandbox = async (
241
390
  sandboxId: string,
242
391
  options?: TOptions
243
392
  ): Promise<SandboxSnapshot> => {
244
393
  return this.snapshot(sandboxId, options);
245
- },
246
- restoreSandbox: async (
394
+ };
395
+ ops.deleteSandboxSnapshot = async (
396
+ snapshot: SandboxSnapshot
397
+ ): Promise<void> => {
398
+ await this.deleteSnapshot(snapshot);
399
+ };
400
+ }
401
+ if (supported.has("restore")) {
402
+ ops.restoreSandbox = async (
247
403
  snapshot: SandboxSnapshot,
248
404
  options?: TOptions
249
405
  ): Promise<string> => {
250
406
  return this.restore(snapshot, options);
251
- },
252
- deleteSandboxSnapshot: async (
253
- snapshot: SandboxSnapshot
254
- ): Promise<void> => {
255
- await this.deleteSnapshot(snapshot);
256
- },
257
- forkSandbox: async (
407
+ };
408
+ }
409
+ if (supported.has("fork")) {
410
+ ops.forkSandbox = async (
258
411
  sandboxId: string,
259
412
  options?: TOptions
260
413
  ): Promise<string> => {
261
414
  return this.fork(sandboxId, options);
262
- },
263
- };
264
- const cap = (s: string): string => s.charAt(0).toUpperCase() + s.slice(1);
415
+ };
416
+ }
417
+
418
+ const entries = Object.entries(ops).filter(
419
+ ([, v]) => typeof v === "function"
420
+ );
265
421
  return Object.fromEntries(
266
- Object.entries(ops).map(([k, v]) => [`${prefix}${cap(k)}`, v])
267
- ) as PrefixedSandboxOps<`${TId}${Capitalize<S>}`, TOptions, TCtx>;
422
+ entries.map(([k, v]) => [`${prefix}${cap(k)}`, v])
423
+ ) as PrefixedSandboxOps<`${TId}${Capitalize<S>}`, TOptions, TCtx, TCaps>;
268
424
  }
269
425
  }
@@ -84,6 +84,14 @@ export interface ExecResult {
84
84
  // Capabilities
85
85
  // ============================================================================
86
86
 
87
+ /**
88
+ * Runtime capability flags carried by a {@link Sandbox} instance.
89
+ *
90
+ * These are an orthogonal mechanism to the type-level
91
+ * {@link SandboxCapability} union: this flag bag is for runtime
92
+ * introspection ("does the sandbox support a filesystem?") whereas
93
+ * {@link SandboxCapability} narrows the type-level provider/ops contract.
94
+ */
87
95
  export interface SandboxCapabilities {
88
96
  /** Sandbox supports filesystem operations */
89
97
  filesystem: boolean;
@@ -93,6 +101,24 @@ export interface SandboxCapabilities {
93
101
  persistence: boolean;
94
102
  }
95
103
 
104
+ /**
105
+ * Type-level capability vocabulary for {@link SandboxProvider} and
106
+ * {@link SandboxOps}. Adapters declare the subset they actually support; the
107
+ * conditional types on each contract gate the corresponding methods so
108
+ * unsupported calls become a compile-time error rather than a runtime
109
+ * {@link SandboxNotSupportedError}.
110
+ *
111
+ * `pause` and `resume` are split because some adapters might support one
112
+ * direction without the other. The `snapshot` cap covers both `snapshot()`
113
+ * and `deleteSnapshot()` since they always travel together in practice.
114
+ */
115
+ export type SandboxCapability =
116
+ | "pause"
117
+ | "resume"
118
+ | "snapshot"
119
+ | "restore"
120
+ | "fork";
121
+
96
122
  // ============================================================================
97
123
  // Sandbox
98
124
  // ============================================================================
@@ -145,88 +171,196 @@ export interface SandboxCreateResult {
145
171
  sandbox: Sandbox;
146
172
  }
147
173
 
148
- export interface SandboxProvider<
149
- TOptions extends SandboxCreateOptions = SandboxCreateOptions,
150
- TSandbox extends Sandbox = Sandbox,
174
+ /**
175
+ * Internal helper: drop keys whose value is `never` from an object type.
176
+ *
177
+ * Used by the capability-gated contracts below so that an absent capability
178
+ * removes the corresponding key entirely, instead of leaving a required
179
+ * field with type `never` (which would make implementations impossible).
180
+ */
181
+ type OmitNever<T> = {
182
+ [K in keyof T as [T[K]] extends [never] ? never : K]: T[K];
183
+ };
184
+
185
+ /**
186
+ * Capability-gated provider lifecycle methods.
187
+ *
188
+ * Each field becomes `never` when its capability is absent from `TCaps`;
189
+ * the wrapping `OmitNever` removes those keys entirely, so the method
190
+ * isn't part of the type surface for adapters that don't support it.
191
+ */
192
+ type SandboxProviderCapMethods<
193
+ TOptions extends SandboxCreateOptions,
194
+ TSandbox extends Sandbox,
195
+ TCaps extends SandboxCapability,
196
+ > = OmitNever<{
197
+ pause: "pause" extends TCaps
198
+ ? (sandboxId: string, ttlSeconds?: number) => Promise<void>
199
+ : never;
200
+ resume: "resume" extends TCaps ? (sandboxId: string) => Promise<void> : never;
201
+ snapshot: "snapshot" extends TCaps
202
+ ? (sandboxId: string, options?: TOptions) => Promise<SandboxSnapshot>
203
+ : never;
204
+ deleteSnapshot: "snapshot" extends TCaps
205
+ ? (snapshot: SandboxSnapshot) => Promise<void>
206
+ : never;
207
+ restore: "restore" extends TCaps
208
+ ? (snapshot: SandboxSnapshot, options?: TOptions) => Promise<TSandbox>
209
+ : never;
210
+ fork: "fork" extends TCaps
211
+ ? (sandboxId: string, options?: TOptions) => Promise<TSandbox>
212
+ : never;
213
+ }>;
214
+
215
+ /**
216
+ * Always-present provider lifecycle methods. These do not depend on the
217
+ * capability set and are required by every adapter.
218
+ */
219
+ interface SandboxProviderBase<
220
+ TOptions extends SandboxCreateOptions,
221
+ TSandbox extends Sandbox,
222
+ TCaps extends SandboxCapability,
151
223
  > {
152
224
  readonly id: string;
153
225
  readonly capabilities: SandboxCapabilities;
226
+ /**
227
+ * Runtime-introspectable list of supported capabilities.
228
+ *
229
+ * Constrained to `ReadonlySet<TCaps & SandboxCapability>` so the runtime
230
+ * set cannot include capabilities not declared at the type level — a
231
+ * provider typed as `SandboxProvider<…, never>` cannot ship a runtime
232
+ * set that contains `"pause"`, etc.
233
+ *
234
+ * The other direction (type declares a cap, runtime set omits it)
235
+ * cannot be enforced by TypeScript alone; adapters should derive both
236
+ * `TCaps` and the runtime set from the same `as const` array (see
237
+ * `SandboxManager`'s constructor-time consistency check) so the two
238
+ * surfaces cannot drift.
239
+ */
240
+ readonly supportedCapabilities: ReadonlySet<TCaps & SandboxCapability>;
154
241
 
155
242
  create(options?: TOptions): Promise<SandboxCreateResult>;
156
243
  get(sandboxId: string): Promise<TSandbox>;
157
244
  destroy(sandboxId: string): Promise<void>;
158
- pause(sandboxId: string, ttlSeconds?: number): Promise<void>;
159
- /** Resume a paused sandbox. No-op if already running. */
160
- resume(sandboxId: string): Promise<void>;
161
- /**
162
- * Capture a snapshot of a running sandbox. `options` is a per-call override
163
- * merged on top of the provider's static defaults.
164
- */
165
- snapshot(sandboxId: string, options?: TOptions): Promise<SandboxSnapshot>;
166
- /**
167
- * Restore a sandbox from a snapshot. `options` is a per-call override
168
- * merged on top of the provider's static defaults.
169
- */
170
- restore(snapshot: SandboxSnapshot, options?: TOptions): Promise<Sandbox>;
171
- /** Delete a previously captured snapshot. No-op if already deleted. */
172
- deleteSnapshot(snapshot: SandboxSnapshot): Promise<void>;
173
- /**
174
- * Fork a running sandbox into a new one. `options` is a per-call override
175
- * merged on top of the provider's static defaults.
176
- */
177
- fork(sandboxId: string, options?: TOptions): Promise<Sandbox>;
178
245
  }
179
246
 
247
+ /**
248
+ * Provider-side sandbox lifecycle contract.
249
+ *
250
+ * Generic over an optional capability set (`TCaps`). Each capability gates
251
+ * a specific method: when the cap is absent the corresponding key is
252
+ * **removed** from the type entirely, so calling it produces a TypeScript
253
+ * error at the call site instead of a runtime
254
+ * {@link SandboxNotSupportedError}.
255
+ *
256
+ * The default `TCaps = SandboxCapability` resolves to the full union, so
257
+ * existing usages that only pass `TOptions` / `TSandbox` continue to see
258
+ * the full method surface (backwards compatible).
259
+ *
260
+ * Adapters that don't support a method should narrow `TCaps` accordingly:
261
+ *
262
+ * - In-memory / E2B: `SandboxCapability` (default — all caps present).
263
+ * - Bedrock Code Interpreter / Daytona: `never` (only base ops).
264
+ * - Bedrock AgentCore Runtime: `"pause" | "resume"`.
265
+ */
266
+ export type SandboxProvider<
267
+ TOptions extends SandboxCreateOptions = SandboxCreateOptions,
268
+ TSandbox extends Sandbox = Sandbox,
269
+ TCaps extends SandboxCapability = SandboxCapability,
270
+ > = SandboxProviderBase<TOptions, TSandbox, TCaps> &
271
+ SandboxProviderCapMethods<TOptions, TSandbox, TCaps>;
272
+
180
273
  // ============================================================================
181
274
  // SandboxOps — workflow-side activity interface (like ThreadOps)
182
275
  // ============================================================================
183
276
 
184
- export interface SandboxOps<
185
- TOptions extends SandboxCreateOptions = SandboxCreateOptions,
186
- TCtx = unknown,
277
+ /**
278
+ * Capability-gated workflow-side methods. Mirrors the provider's gating:
279
+ * keys whose capability is absent from `TCaps` are removed from the type.
280
+ */
281
+ type SandboxOpsCapMethods<
282
+ TOptions extends SandboxCreateOptions,
283
+ TCaps extends SandboxCapability,
284
+ > = OmitNever<{
285
+ pauseSandbox: "pause" extends TCaps
286
+ ? (sandboxId: string) => Promise<void>
287
+ : never;
288
+ resumeSandbox: "resume" extends TCaps
289
+ ? (sandboxId: string) => Promise<void>
290
+ : never;
291
+ snapshotSandbox: "snapshot" extends TCaps
292
+ ? (sandboxId: string, options?: TOptions) => Promise<SandboxSnapshot>
293
+ : never;
294
+ deleteSandboxSnapshot: "snapshot" extends TCaps
295
+ ? (snapshot: SandboxSnapshot) => Promise<void>
296
+ : never;
297
+ restoreSandbox: "restore" extends TCaps
298
+ ? (snapshot: SandboxSnapshot, options?: TOptions) => Promise<string>
299
+ : never;
300
+ forkSandbox: "fork" extends TCaps
301
+ ? (sandboxId: string, options?: TOptions) => Promise<string>
302
+ : never;
303
+ }>;
304
+
305
+ /**
306
+ * Always-present workflow-side lifecycle methods.
307
+ */
308
+ interface SandboxOpsBase<
309
+ TOptions extends SandboxCreateOptions,
310
+ TCtx,
187
311
  > {
188
312
  createSandbox(
189
313
  options?: TOptions,
190
314
  ctx?: TCtx
191
315
  ): Promise<{ sandboxId: string } | null>;
192
316
  destroySandbox(sandboxId: string): Promise<void>;
193
- pauseSandbox(sandboxId: string): Promise<void>;
194
- /** Resume a paused sandbox. No-op if already running. */
195
- resumeSandbox(sandboxId: string): Promise<void>;
196
- /** Capture a snapshot. `options` is a per-call override merged on top of provider defaults. */
197
- snapshotSandbox(
198
- sandboxId: string,
199
- options?: TOptions
200
- ): Promise<SandboxSnapshot>;
201
- /** Create a fresh sandbox from a snapshot. `options` is a per-call override merged on top of provider defaults. */
202
- restoreSandbox(
203
- snapshot: SandboxSnapshot,
204
- options?: TOptions
205
- ): Promise<string>;
206
- /** Delete a previously captured snapshot. No-op if already deleted. */
207
- deleteSandboxSnapshot(snapshot: SandboxSnapshot): Promise<void>;
208
- /** Fork a running sandbox. `options` is a per-call override merged on top of provider defaults. */
209
- forkSandbox(sandboxId: string, options?: TOptions): Promise<string>;
210
317
  }
211
318
 
319
+ /**
320
+ * Workflow-side counterpart to {@link SandboxProvider}. Exposed as a set of
321
+ * Temporal activities and consumed by `createSession`'s `sandboxOps` field
322
+ * and by `defineSubagent`'s `sandbox.proxy`.
323
+ *
324
+ * Generic over a capability set (`TCaps`) — same semantics as the provider:
325
+ * keys whose capability is absent are removed from the type, so calling
326
+ * them is a TypeScript error rather than a runtime throw. The default
327
+ * `TCaps = SandboxCapability` keeps the full method surface for existing
328
+ * consumers.
329
+ */
330
+ export type SandboxOps<
331
+ TOptions extends SandboxCreateOptions = SandboxCreateOptions,
332
+ TCtx = unknown,
333
+ TCaps extends SandboxCapability = SandboxCapability,
334
+ > = SandboxOpsBase<TOptions, TCtx> & SandboxOpsCapMethods<TOptions, TCaps>;
335
+
212
336
  /**
213
337
  * Maps generic {@link SandboxOps} method names to adapter-prefixed names.
214
338
  *
339
+ * Inherits the capability gating from {@link SandboxOps}: when `TCaps` omits
340
+ * a capability the prefixed key carries the `never` type so call sites are
341
+ * type-protected.
342
+ *
215
343
  * @example
216
344
  * ```typescript
217
345
  * type InMemOps = PrefixedSandboxOps<"inMemory">;
218
- * // → { inMemoryCreateSandbox, inMemoryDestroySandbox, inMemorySnapshotSandbox }
346
+ * // → { inMemoryCreateSandbox, inMemoryDestroySandbox, inMemorySnapshotSandbox, }
219
347
  * ```
220
348
  */
221
349
  export type PrefixedSandboxOps<
222
350
  TPrefix extends string,
223
351
  TOptions extends SandboxCreateOptions = SandboxCreateOptions,
224
352
  TCtx = unknown,
353
+ TCaps extends SandboxCapability = SandboxCapability,
225
354
  > = {
226
355
  [K in keyof SandboxOps<
227
356
  TOptions,
228
- TCtx
229
- > as `${TPrefix}${Capitalize<K & string>}`]: SandboxOps<TOptions, TCtx>[K];
357
+ TCtx,
358
+ TCaps
359
+ > as `${TPrefix}${Capitalize<K & string>}`]: SandboxOps<
360
+ TOptions,
361
+ TCtx,
362
+ TCaps
363
+ >[K];
230
364
  };
231
365
 
232
366
  // ============================================================================
@@ -235,6 +369,15 @@ export type PrefixedSandboxOps<
235
369
 
236
370
  import { ApplicationFailure } from "@temporalio/common";
237
371
 
372
+ /**
373
+ * Thrown by adapters that still surface an unsupported method at runtime.
374
+ *
375
+ * After the capability-generic refactor most adapters drop their
376
+ * unsupported methods entirely so the type system rejects them at call
377
+ * sites. This symbol is still exported so consumers running against older
378
+ * adapter versions can keep their backwards-compatible error-handling
379
+ * paths until they finish migrating.
380
+ */
238
381
  export class SandboxNotSupportedError extends ApplicationFailure {
239
382
  constructor(operation: string) {
240
383
  super(
@@ -5,6 +5,7 @@ export type {
5
5
  PrefixedThreadOps,
6
6
  ScopedPrefix,
7
7
  SessionConfig,
8
+ SessionRequiredCaps,
8
9
  SessionResult,
9
10
  ZeitlichSession,
10
11
  } from "./types";