zeitlich 0.2.40 → 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 (134) hide show
  1. package/README.md +12 -1
  2. package/dist/{activities-CvUrG3YG.d.cts → activities-Coafq5zr.d.cts} +2 -2
  3. package/dist/{activities-CULxRzJ1.d.ts → activities-CrN-ghLo.d.ts} +2 -2
  4. package/dist/adapters/sandbox/daytona/index.cjs +4 -23
  5. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  6. package/dist/adapters/sandbox/daytona/index.d.cts +18 -86
  7. package/dist/adapters/sandbox/daytona/index.d.ts +18 -86
  8. package/dist/adapters/sandbox/daytona/index.js +4 -23
  9. package/dist/adapters/sandbox/daytona/index.js.map +1 -1
  10. package/dist/adapters/sandbox/daytona/workflow.cjs +1 -7
  11. package/dist/adapters/sandbox/daytona/workflow.cjs.map +1 -1
  12. package/dist/adapters/sandbox/daytona/workflow.d.cts +9 -2
  13. package/dist/adapters/sandbox/daytona/workflow.d.ts +9 -2
  14. package/dist/adapters/sandbox/daytona/workflow.js +1 -7
  15. package/dist/adapters/sandbox/daytona/workflow.js.map +1 -1
  16. package/dist/adapters/sandbox/e2b/index.cjs +21 -3
  17. package/dist/adapters/sandbox/e2b/index.cjs.map +1 -1
  18. package/dist/adapters/sandbox/e2b/index.d.cts +48 -7
  19. package/dist/adapters/sandbox/e2b/index.d.ts +48 -7
  20. package/dist/adapters/sandbox/e2b/index.js +22 -5
  21. package/dist/adapters/sandbox/e2b/index.js.map +1 -1
  22. package/dist/adapters/sandbox/e2b/workflow.cjs.map +1 -1
  23. package/dist/adapters/sandbox/e2b/workflow.d.cts +4 -2
  24. package/dist/adapters/sandbox/e2b/workflow.d.ts +4 -2
  25. package/dist/adapters/sandbox/e2b/workflow.js.map +1 -1
  26. package/dist/adapters/sandbox/inmemory/index.cjs +11 -0
  27. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
  28. package/dist/adapters/sandbox/inmemory/index.d.cts +11 -3
  29. package/dist/adapters/sandbox/inmemory/index.d.ts +11 -3
  30. package/dist/adapters/sandbox/inmemory/index.js +11 -1
  31. package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
  32. package/dist/adapters/sandbox/inmemory/workflow.cjs.map +1 -1
  33. package/dist/adapters/sandbox/inmemory/workflow.d.cts +4 -2
  34. package/dist/adapters/sandbox/inmemory/workflow.d.ts +4 -2
  35. package/dist/adapters/sandbox/inmemory/workflow.js.map +1 -1
  36. package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
  37. package/dist/adapters/thread/anthropic/index.d.cts +6 -6
  38. package/dist/adapters/thread/anthropic/index.d.ts +6 -6
  39. package/dist/adapters/thread/anthropic/index.js.map +1 -1
  40. package/dist/adapters/thread/anthropic/workflow.d.cts +6 -6
  41. package/dist/adapters/thread/anthropic/workflow.d.ts +6 -6
  42. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  43. package/dist/adapters/thread/google-genai/index.d.cts +6 -6
  44. package/dist/adapters/thread/google-genai/index.d.ts +6 -6
  45. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  46. package/dist/adapters/thread/google-genai/workflow.d.cts +6 -6
  47. package/dist/adapters/thread/google-genai/workflow.d.ts +6 -6
  48. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  49. package/dist/adapters/thread/langchain/index.d.cts +6 -6
  50. package/dist/adapters/thread/langchain/index.d.ts +6 -6
  51. package/dist/adapters/thread/langchain/index.js.map +1 -1
  52. package/dist/adapters/thread/langchain/workflow.d.cts +6 -6
  53. package/dist/adapters/thread/langchain/workflow.d.ts +6 -6
  54. package/dist/index.cjs +316 -119
  55. package/dist/index.cjs.map +1 -1
  56. package/dist/index.d.cts +93 -17
  57. package/dist/index.d.ts +93 -17
  58. package/dist/index.js +317 -120
  59. package/dist/index.js.map +1 -1
  60. package/dist/{proxy-5EbwzaY4.d.cts → proxy-Bf7uI-Hw.d.cts} +1 -1
  61. package/dist/{proxy-wZufFfBh.d.ts → proxy-COqA95FW.d.ts} +1 -1
  62. package/dist/{thread-manager-BqBAIsED.d.ts → thread-manager-BhkOyQ1I.d.ts} +2 -2
  63. package/dist/{thread-manager-BNiIt5r8.d.ts → thread-manager-Bi1XlbpJ.d.ts} +2 -2
  64. package/dist/{thread-manager-DF8WuCRs.d.cts → thread-manager-BsLO3Fgc.d.cts} +2 -2
  65. package/dist/{thread-manager-BoN5DOvG.d.cts → thread-manager-wRVVBFgj.d.cts} +2 -2
  66. package/dist/{types-C7OoY7h8.d.ts → types-BkX4HLzi.d.ts} +1 -1
  67. package/dist/{types-CuISs0Ub.d.cts → types-C66-BVBr.d.cts} +1 -1
  68. package/dist/types-CJ7tCdl6.d.cts +266 -0
  69. package/dist/types-CJ7tCdl6.d.ts +266 -0
  70. package/dist/{types-DeQH84C_.d.ts → types-CdALEF3z.d.cts} +342 -23
  71. package/dist/{types-Cn2r3ol3.d.cts → types-ChAy_jSP.d.ts} +342 -23
  72. package/dist/types-CjY93AWZ.d.cts +84 -0
  73. package/dist/types-gVa5XCWD.d.ts +84 -0
  74. package/dist/{workflow-DhplIN65.d.cts → workflow-BwT5EybR.d.ts} +7 -6
  75. package/dist/{workflow-C2MZZj5K.d.ts → workflow-DMmiaw6w.d.cts} +7 -6
  76. package/dist/workflow.cjs +138 -77
  77. package/dist/workflow.cjs.map +1 -1
  78. package/dist/workflow.d.cts +4 -4
  79. package/dist/workflow.d.ts +4 -4
  80. package/dist/workflow.js +139 -78
  81. package/dist/workflow.js.map +1 -1
  82. package/package.json +17 -33
  83. package/src/adapters/sandbox/daytona/index.ts +25 -48
  84. package/src/adapters/sandbox/daytona/proxy.ts +7 -8
  85. package/src/adapters/sandbox/e2b/README.md +81 -0
  86. package/src/adapters/sandbox/e2b/index.ts +53 -11
  87. package/src/adapters/sandbox/e2b/keep-alive.test.ts +115 -0
  88. package/src/adapters/sandbox/e2b/proxy.ts +3 -2
  89. package/src/adapters/sandbox/e2b/types.ts +34 -2
  90. package/src/adapters/sandbox/inmemory/index.ts +21 -1
  91. package/src/adapters/sandbox/inmemory/proxy.ts +7 -3
  92. package/src/index.ts +1 -1
  93. package/src/lib/activity.ts +5 -0
  94. package/src/lib/sandbox/capability-types.test.ts +859 -0
  95. package/src/lib/sandbox/index.ts +1 -0
  96. package/src/lib/sandbox/manager.ts +187 -31
  97. package/src/lib/sandbox/types.ts +189 -46
  98. package/src/lib/session/index.ts +1 -0
  99. package/src/lib/session/session.integration.test.ts +58 -0
  100. package/src/lib/session/session.ts +109 -50
  101. package/src/lib/session/types.ts +189 -8
  102. package/src/lib/subagent/handler.ts +66 -43
  103. package/src/lib/subagent/subagent.integration.test.ts +2 -0
  104. package/src/lib/subagent/types.ts +492 -16
  105. package/src/lib/subagent/workflow.ts +11 -1
  106. package/src/lib/tool-router/auto-append-sandbox.integration.test.ts +158 -0
  107. package/src/lib/tool-router/index.ts +1 -1
  108. package/src/lib/tool-router/with-sandbox.ts +45 -2
  109. package/src/lib/virtual-fs/filesystem.ts +41 -16
  110. package/src/lib/virtual-fs/types.ts +19 -0
  111. package/src/lib/virtual-fs/virtual-fs.test.ts +204 -1
  112. package/src/tools/read-file/handler.test.ts +83 -0
  113. package/src/workflow.ts +3 -0
  114. package/tsup.config.ts +0 -4
  115. package/dist/adapters/sandbox/bedrock/index.cjs +0 -457
  116. package/dist/adapters/sandbox/bedrock/index.cjs.map +0 -1
  117. package/dist/adapters/sandbox/bedrock/index.d.cts +0 -25
  118. package/dist/adapters/sandbox/bedrock/index.d.ts +0 -25
  119. package/dist/adapters/sandbox/bedrock/index.js +0 -454
  120. package/dist/adapters/sandbox/bedrock/index.js.map +0 -1
  121. package/dist/adapters/sandbox/bedrock/workflow.cjs +0 -36
  122. package/dist/adapters/sandbox/bedrock/workflow.cjs.map +0 -1
  123. package/dist/adapters/sandbox/bedrock/workflow.d.cts +0 -29
  124. package/dist/adapters/sandbox/bedrock/workflow.d.ts +0 -29
  125. package/dist/adapters/sandbox/bedrock/workflow.js +0 -34
  126. package/dist/adapters/sandbox/bedrock/workflow.js.map +0 -1
  127. package/dist/types-DAsQ21Rt.d.ts +0 -74
  128. package/dist/types-lm8tMNJQ.d.cts +0 -74
  129. package/dist/types-yx0LzPGn.d.cts +0 -173
  130. package/dist/types-yx0LzPGn.d.ts +0 -173
  131. package/src/adapters/sandbox/bedrock/filesystem.ts +0 -340
  132. package/src/adapters/sandbox/bedrock/index.ts +0 -274
  133. package/src/adapters/sandbox/bedrock/proxy.ts +0 -59
  134. package/src/adapters/sandbox/bedrock/types.ts +0 -24
@@ -11,7 +11,12 @@ import type {
11
11
  SandboxInit,
12
12
  SubagentSandboxShutdown,
13
13
  } from "../lifecycle";
14
- import type { SandboxOps, SandboxSnapshot } from "../sandbox/types";
14
+ import type {
15
+ SandboxCapability,
16
+ SandboxCreateOptions,
17
+ SandboxOps,
18
+ SandboxSnapshot,
19
+ } from "../sandbox/types";
15
20
 
16
21
  /**
17
22
  * Subset of {@link ChildWorkflowOptions} that callers may override when a
@@ -80,6 +85,469 @@ export type SubagentContext =
80
85
  export type InferSubagentResult<T extends SubagentConfig> =
81
86
  T extends SubagentConfig<infer S> ? z.infer<S> : null;
82
87
 
88
+ // ============================================================================
89
+ // Subagent sandbox-lifecycle decision table (SSOT)
90
+ //
91
+ // `createSubagentHandler` (`src/lib/subagent/handler.ts`) auto-injects
92
+ // `sandboxShutdown` for some `(source, continuation, init, shutdown)`
93
+ // combinations (`mustSurvive` + `alreadySurvives` rules). The child
94
+ // session then dispatches gated sandbox methods based on the resolved
95
+ // `(sandbox.mode, sandboxShutdown)` pair (`src/lib/session/session.ts`).
96
+ //
97
+ // Two surfaces have to agree on what gated methods may fire for a given
98
+ // config:
99
+ //
100
+ // 1. The runtime — what the parent injects + what the child session
101
+ // dispatches.
102
+ // 2. The type level — what caps the proxy field has to advertise.
103
+ //
104
+ // `SubagentRequiredCaps<C>` is the SSOT for the type level.
105
+ // `resolveSubagentLifecycle(cfg)` (in `handler.ts`) is the SSOT for the
106
+ // runtime; it returns the specific `(mode, shutdown)` to inject and is
107
+ // the only place the auto-injection rules live. Adding a new runtime
108
+ // branch in `handler.ts` requires extending **both** — the matrix in
109
+ // `src/lib/sandbox/capability-types.test.ts` then locks the agreement.
110
+ //
111
+ // `Continuation`-only / `Shutdown`-only sub-types live below as helpers
112
+ // `_ChildModeCaps` / `_ChildShutdownCaps` so each branch reads cleanly.
113
+ // ============================================================================
114
+
115
+ /** Shutdown classes that the runtime treats equivalently for cap purposes. */
116
+ type _ShutdownPauseLike = "pause" | "pause-until-parent-close";
117
+ type _ShutdownKeepLike = "keep" | "keep-until-parent-close";
118
+
119
+ /**
120
+ * Caps the child session's sandbox-init dispatch invokes for a given
121
+ * `(mode, shutdown)`. Mirror of `src/lib/session/session.ts:230-314`.
122
+ */
123
+ type _ChildModeCaps<Mode, Shutdown> =
124
+ // mode "fork" → forkSandbox
125
+ | (Mode extends "fork" ? "fork" : never)
126
+ // mode "from-snapshot" → restoreSandbox
127
+ | (Mode extends "from-snapshot" ? "restore" : never)
128
+ // mode "continue" + shutdown "pause-until-parent-close" → resumeSandbox
129
+ | (Mode extends "continue"
130
+ ? Shutdown extends "pause-until-parent-close"
131
+ ? "resume"
132
+ : never
133
+ : never);
134
+
135
+ /**
136
+ * Caps the child session's exit dispatch invokes for a given `shutdown`.
137
+ * Only fires when `sandboxOwned` is true (mode != "inherit"). Mirror of
138
+ * `src/lib/session/session.ts:598-615`.
139
+ */
140
+ type _ChildShutdownCaps<Mode, Shutdown> =
141
+ // mode="inherit" → sandboxOwned=false → no exit-shutdown caps fire
142
+ Mode extends "inherit"
143
+ ? never
144
+ :
145
+ | (Shutdown extends _ShutdownPauseLike ? "pause" : never)
146
+ | (Shutdown extends "snapshot" ? "snapshot" : never);
147
+
148
+ /**
149
+ * Caps captured on entry for `mode: "new"` + `shutdown: "snapshot"`
150
+ * (the seeding-base-snapshot path). Mirror of
151
+ * `src/lib/session/session.ts:316-317`.
152
+ */
153
+ type _ChildSeedCaps<Mode, Shutdown> = Mode extends "new"
154
+ ? Shutdown extends "snapshot"
155
+ ? "snapshot"
156
+ : never
157
+ : never;
158
+
159
+ /**
160
+ * Total caps a single child-session invocation calls for the specific
161
+ * `(mode, shutdown)` pair the parent passes. The proxy field's required
162
+ * cap union is the union of this across every `(mode, shutdown)` the
163
+ * runtime might inject for the given config.
164
+ */
165
+ type _ChildSessionCaps<Mode, Shutdown> =
166
+ | _ChildModeCaps<Mode, Shutdown>
167
+ | _ChildShutdownCaps<Mode, Shutdown>
168
+ | _ChildSeedCaps<Mode, Shutdown>;
169
+
170
+ /**
171
+ * Resolves the user's `shutdown` value through the auto-injection rules
172
+ * for `(source: "own", continuation: "continue")`. Mirror of
173
+ * `src/lib/subagent/handler.ts:373-389`:
174
+ *
175
+ * - `pause` / `pause-until-parent-close` / `keep` / `keep-until-parent-close`
176
+ * → propagate (`alreadySurvives = true`).
177
+ * - everything else (undefined, "destroy", "snapshot") → injected
178
+ * `"pause"` (subsequent calls) or `"pause-until-parent-close"`
179
+ * (creator first call). Type-level: union both.
180
+ */
181
+ type _ContinueShutdown<S> = S extends _ShutdownPauseLike | _ShutdownKeepLike
182
+ ? S
183
+ : "pause" | "pause-until-parent-close";
184
+
185
+ /**
186
+ * Resolves the user's `shutdown` value through the auto-injection rules
187
+ * for `(source: "own", continuation: "fork", init: "once")`. Same shape
188
+ * as `_ContinueShutdown` because both are `mustSurvive` paths.
189
+ */
190
+ type _ForkOnceShutdown<S> = _ContinueShutdown<S>;
191
+
192
+ /**
193
+ * Modes the child session may be invoked under for `(source: "own",
194
+ * continuation: "continue")`. First call has no `baseSandboxId` (mode
195
+ * "new"); subsequent calls reuse it (mode "continue"). The type takes
196
+ * the union — the matrix can't tell first vs. subsequent statically.
197
+ */
198
+ type _OwnContinueModes = "new" | "continue";
199
+
200
+ /**
201
+ * Modes the child session may be invoked under for `(source: "own",
202
+ * continuation: "fork")`. Same first-vs-subsequent shape as continue.
203
+ */
204
+ type _OwnForkModes = "new" | "fork";
205
+
206
+ /**
207
+ * Modes the child session may be invoked under for `(source: "own",
208
+ * continuation: "snapshot")`. First call (no base snapshot yet) uses
209
+ * "new"; subsequent calls (or `init: "once"` after the first creator
210
+ * publishes a base) use "from-snapshot".
211
+ */
212
+ type _OwnSnapshotModes = "new" | "from-snapshot";
213
+
214
+ /**
215
+ * Caps required on a subagent's `proxy` for the parent's own gated
216
+ * calls. The parent only ever calls `destroySandbox` (base) and
217
+ * `deleteSandboxSnapshot` (`continuation: "snapshot"` cleanup).
218
+ */
219
+ type _ParentLocalCaps<C> = C extends { continuation: "snapshot" }
220
+ ? "snapshot"
221
+ : never;
222
+
223
+ /**
224
+ * **SSOT type.** The full cap union a subagent's `proxy` must expose,
225
+ * derived from `(source, continuation, init, shutdown)`.
226
+ *
227
+ * The shape mirrors the rows of the runtime decision table in
228
+ * `resolveSubagentLifecycle` (`src/lib/subagent/handler.ts`): each
229
+ * branch here corresponds to exactly one runtime branch, and adding a
230
+ * new runtime branch requires adding a matching branch here. The
231
+ * `(adapter × continuation × shutdown × init × source)` matrix in
232
+ * `src/lib/sandbox/capability-types.test.ts` enforces agreement.
233
+ */
234
+ export type SubagentRequiredCaps<C> = C extends "none"
235
+ ? never
236
+ : C extends { source: "inherit"; continuation: "continue" }
237
+ ? // mode="inherit", sandboxOwned=false → no gated calls regardless
238
+ // of shutdown value.
239
+ never
240
+ : C extends { source: "inherit"; continuation: "fork" }
241
+ ? // mode="fork" + user shutdown propagates verbatim (no
242
+ // auto-injection on the inherit path).
243
+ | "fork"
244
+ | _ChildSessionCaps<
245
+ "fork",
246
+ C extends { shutdown: infer S } ? S : "destroy"
247
+ >
248
+ | _ParentLocalCaps<C>
249
+ : C extends { source: "own"; continuation: "snapshot" }
250
+ ? // override = "snapshot" always; modes vary across calls.
251
+ | _ChildSessionCaps<_OwnSnapshotModes, "snapshot">
252
+ | _ParentLocalCaps<C>
253
+ : C extends { source: "own"; continuation: "continue" }
254
+ ? // mustSurvive=true; injection rules apply.
255
+ | _ChildSessionCaps<
256
+ _OwnContinueModes,
257
+ _ContinueShutdown<
258
+ C extends { shutdown: infer S } ? S : undefined
259
+ >
260
+ >
261
+ | _ParentLocalCaps<C>
262
+ : C extends {
263
+ source: "own";
264
+ continuation: "fork";
265
+ init?: infer I;
266
+ }
267
+ ? // mustSurvive iff init=once.
268
+ | "fork"
269
+ | _ChildSessionCaps<
270
+ _OwnForkModes,
271
+ I extends "once"
272
+ ? _ForkOnceShutdown<
273
+ C extends { shutdown: infer S } ? S : undefined
274
+ >
275
+ : C extends { shutdown: infer S }
276
+ ? S extends undefined
277
+ ? "destroy"
278
+ : S
279
+ : "destroy"
280
+ >
281
+ | _ParentLocalCaps<C>
282
+ : never;
283
+
284
+ /**
285
+ * Backwards-compatible alias retained for external callers that imported
286
+ * the old name. Resolves through the SSOT against a synthetic
287
+ * `{ source: "own", continuation: C }` config.
288
+ *
289
+ * @deprecated Use `SubagentRequiredCaps<C>` against the full subagent
290
+ * sandbox config — `continuation` alone misses `shutdown` and `init`,
291
+ * which is why the previous mapping under-rejected `fork`+`pause` and
292
+ * `continue`+auto-injected pause.
293
+ */
294
+ export type SubagentContinuationCaps<C extends SubagentContinuation> =
295
+ C extends "snapshot"
296
+ ? "snapshot" | "restore"
297
+ : C extends "fork"
298
+ ? "fork"
299
+ : never;
300
+
301
+ // ============================================================================
302
+ // Subagent lifecycle SSOT — runtime mirror of `SubagentRequiredCaps`
303
+ //
304
+ // `resolveSubagentLifecycle` is the runtime side of the same table
305
+ // `SubagentRequiredCaps` reads at the type level. Whenever
306
+ // `createSubagentHandler` (`src/lib/subagent/handler.ts`) needs to
307
+ // decide which `(sandbox.mode, sandboxShutdown)` to inject for the
308
+ // child, it calls this function — so the auto-injection rules
309
+ // (`mustSurvive`, `alreadySurvives`, snapshot override) live in
310
+ // **one** place. Adding a new branch to the runtime means changing
311
+ // this function AND the matching branch in `SubagentRequiredCaps`;
312
+ // the `(adapter × continuation × shutdown × init × source)` matrix
313
+ // in `src/lib/sandbox/capability-types.test.ts` enforces the agree.
314
+ // ============================================================================
315
+
316
+ /**
317
+ * Resolved sandbox config after normalising defaults. The handler
318
+ * passes one of these into `resolveSubagentLifecycle`.
319
+ */
320
+ export interface ResolvedSubagentSandboxConfig {
321
+ source: "none" | "inherit" | "own";
322
+ init: "per-call" | "once";
323
+ continuation: "continue" | "fork" | "snapshot";
324
+ shutdown?: SubagentSandboxShutdown;
325
+ }
326
+
327
+ /**
328
+ * Output of `resolveSubagentLifecycle`. The handler reads
329
+ * `shutdownOverride` to decide what to forward to the child workflow,
330
+ * and `mustSurvive` / `isLazyCreator` to drive the in-handler
331
+ * bookkeeping (pendingDestroys, persistentSandboxes, etc.).
332
+ */
333
+ export interface ResolvedSubagentLifecycle {
334
+ /**
335
+ * Sandbox shutdown the parent forwards to the child workflow. May
336
+ * be auto-injected (`"pause"` / `"pause-until-parent-close"` /
337
+ * `"snapshot"`) when the user's literal would not survive long
338
+ * enough for the parent's continuation strategy.
339
+ */
340
+ shutdownOverride: SubagentSandboxShutdown | undefined;
341
+ /**
342
+ * Whether the parent must keep the sandbox alive past the child
343
+ * session's exit. Drives the `pendingDestroys` map population.
344
+ */
345
+ mustSurvive: boolean;
346
+ }
347
+
348
+ /**
349
+ * Returns true iff the user's `shutdown` already keeps the sandbox
350
+ * alive (so the handler doesn't need to auto-inject one).
351
+ *
352
+ * Mirror of the type-level `_ShutdownPauseLike` / `_ShutdownKeepLike`
353
+ * checks above.
354
+ */
355
+ function isSurvivalShutdown(s: SubagentSandboxShutdown | undefined): boolean {
356
+ return (
357
+ s === "pause" ||
358
+ s === "pause-until-parent-close" ||
359
+ s === "keep" ||
360
+ s === "keep-until-parent-close"
361
+ );
362
+ }
363
+
364
+ /**
365
+ * The single runtime decision-table consumer. Returns the shutdown the
366
+ * parent should forward to the child plus survival metadata.
367
+ *
368
+ * Branches must agree, one-for-one, with `SubagentRequiredCaps<C>` in
369
+ * this file. The matrix in `capability-types.test.ts` covers each
370
+ * branch.
371
+ */
372
+ export function resolveSubagentLifecycle(
373
+ cfg: ResolvedSubagentSandboxConfig,
374
+ isLazyCreator: boolean
375
+ ): ResolvedSubagentLifecycle {
376
+ // none / inherit: no auto-injection. Handler still calls
377
+ // destroySandbox on child exit when mode=inherit, but
378
+ // `sandboxOwned` stays false in the child session so no exit-
379
+ // shutdown caps fire. Parent's pendingDestroys is driven entirely
380
+ // by the user's shutdown propagating verbatim.
381
+ if (cfg.source !== "own") {
382
+ return {
383
+ shutdownOverride: cfg.shutdown,
384
+ mustSurvive: false,
385
+ };
386
+ }
387
+
388
+ // own + snapshot: handler always overrides shutdown to "snapshot".
389
+ // No survival flag (snapshots are cleaned up via deleteSandboxSnapshot,
390
+ // not via pendingDestroys).
391
+ if (cfg.continuation === "snapshot") {
392
+ return {
393
+ shutdownOverride: "snapshot",
394
+ mustSurvive: false,
395
+ };
396
+ }
397
+
398
+ // own + (continue | fork): mustSurvive iff isLazyCreator OR
399
+ // continuation === "continue" OR (init === "once" + fork).
400
+ const isLazy = cfg.init === "once";
401
+ const mustSurvive =
402
+ isLazyCreator ||
403
+ cfg.continuation === "continue" ||
404
+ (isLazy && cfg.continuation === "fork");
405
+
406
+ if (!mustSurvive) {
407
+ return { shutdownOverride: cfg.shutdown, mustSurvive: false };
408
+ }
409
+
410
+ // mustSurvive: auto-inject only if the user's shutdown doesn't
411
+ // already survive.
412
+ if (isSurvivalShutdown(cfg.shutdown)) {
413
+ return { shutdownOverride: cfg.shutdown, mustSurvive };
414
+ }
415
+ return {
416
+ shutdownOverride: isLazyCreator ? "pause-until-parent-close" : "pause",
417
+ mustSurvive,
418
+ };
419
+ }
420
+
421
+ /** Continuation values supported when `source` is `"inherit"`. */
422
+ type InheritContinuation = "continue" | "fork";
423
+ /** Continuation values supported when `source` is `"own"`. */
424
+ type OwnContinuation = "continue" | "fork" | "snapshot";
425
+ /** Union of every continuation value across both sources. */
426
+ type SubagentContinuation = InheritContinuation | OwnContinuation;
427
+
428
+ /**
429
+ * Caps required on a subagent's `proxy` when `source: "inherit"`,
430
+ * threaded through the SSOT.
431
+ *
432
+ * The `inherit` source has no `init` field, and the `shutdown` field
433
+ * is the user's literal (or `"destroy"` if omitted). We don't have a
434
+ * way to detect "omitted" in the field type itself, so this row
435
+ * resolves the user's `S | undefined` directly — `undefined` is treated
436
+ * as `"destroy"` to match the runtime default in
437
+ * `src/lib/subagent/handler.ts:469`.
438
+ */
439
+ type _InheritCaps<C extends InheritContinuation, S> = SubagentRequiredCaps<{
440
+ source: "inherit";
441
+ continuation: C;
442
+ shutdown: S extends undefined ? "destroy" : S;
443
+ }>;
444
+
445
+ /**
446
+ * Variants for `source: "inherit"`. Continuation × shutdown-presence ×
447
+ * shutdown-literal all distribute, with omitted vs. specified
448
+ * `shutdown` encoded as separate variants — for the same reason
449
+ * `OwnVariant` does (forbid TS from matching a permissive cell when
450
+ * the user omits the field).
451
+ */
452
+ type InheritVariant<TOptions extends SandboxCreateOptions> =
453
+ InheritContinuation extends infer C
454
+ ? C extends InheritContinuation
455
+ ? (
456
+ | { _s: undefined; shutdown?: never }
457
+ | (SubagentSandboxShutdown extends infer SL
458
+ ? SL extends SubagentSandboxShutdown
459
+ ? { _s: SL; shutdown: SL }
460
+ : never
461
+ : never)
462
+ ) extends infer S
463
+ ? S extends {
464
+ _s: SubagentSandboxShutdown | undefined;
465
+ shutdown?: SubagentSandboxShutdown;
466
+ }
467
+ ? Omit<S, "_s"> & {
468
+ source: "inherit";
469
+ continuation: C;
470
+ proxy: (
471
+ scope: string
472
+ ) => SandboxOps<
473
+ TOptions,
474
+ unknown,
475
+ _InheritCaps<C, S["_s"]> & SandboxCapability
476
+ >;
477
+ }
478
+ : never
479
+ : never
480
+ : never
481
+ : never;
482
+
483
+ type _OwnCaps<
484
+ C extends OwnContinuation,
485
+ I extends "per-call" | "once" | undefined,
486
+ S,
487
+ > = SubagentRequiredCaps<{
488
+ source: "own";
489
+ continuation: C;
490
+ init: I extends undefined ? "per-call" : I;
491
+ shutdown: S;
492
+ }>;
493
+
494
+ /**
495
+ * Variants for `source: "own"`. Continuation × init-presence × shutdown-
496
+ * presence × shutdown-literal all distribute, so each cell gets its
497
+ * own variant with a precisely-typed `proxy`.
498
+ *
499
+ * Critical: omitted vs. specified `init` / `shutdown` are encoded as
500
+ * *separate* variants. The omitted variant uses `init?: never` /
501
+ * `shutdown?: never` so the field is forbidden when the user doesn't
502
+ * write one — without this, TS would infer `field?: undefined` and
503
+ * happily match an unrelated permissive variant whose caps don't
504
+ * cover the auto-injection.
505
+ */
506
+ type OwnVariant<TOptions extends SandboxCreateOptions> =
507
+ OwnContinuation extends infer C
508
+ ? C extends OwnContinuation
509
+ ? // Init: omitted (never present) | "per-call" required | "once" required.
510
+ (
511
+ | { _i: undefined; init?: never }
512
+ | { _i: "per-call"; init: "per-call" }
513
+ | { _i: "once"; init: "once" }
514
+ ) extends infer I
515
+ ? I extends {
516
+ _i: "per-call" | "once" | undefined;
517
+ init?: "per-call" | "once";
518
+ }
519
+ ? // Shutdown: omitted | one of the literal values required.
520
+ (
521
+ | { _s: undefined; shutdown?: never }
522
+ | (SubagentSandboxShutdown extends infer SL
523
+ ? SL extends SubagentSandboxShutdown
524
+ ? { _s: SL; shutdown: SL }
525
+ : never
526
+ : never)
527
+ ) extends infer S
528
+ ? S extends {
529
+ _s: SubagentSandboxShutdown | undefined;
530
+ shutdown?: SubagentSandboxShutdown;
531
+ }
532
+ ? Omit<I, "_i"> &
533
+ Omit<S, "_s"> & {
534
+ source: "own";
535
+ continuation: C;
536
+ proxy: (
537
+ scope: string
538
+ ) => SandboxOps<
539
+ TOptions,
540
+ unknown,
541
+ _OwnCaps<C, I["_i"], S["_s"]> & SandboxCapability
542
+ >;
543
+ }
544
+ : never
545
+ : never
546
+ : never
547
+ : never
548
+ : never
549
+ : never;
550
+
83
551
  /**
84
552
  * Sandbox configuration for a subagent.
85
553
  *
@@ -97,22 +565,22 @@ export type InferSubagentResult<T extends SubagentConfig> =
97
565
  * `scope = agentName`, so the returned proxy resolves to the same activity
98
566
  * prefix the child session uses. The parent uses it to destroy lingering
99
567
  * sandboxes and delete stored snapshots at shutdown.
568
+ *
569
+ * The `proxy` field's required `TCaps` is derived from
570
+ * {@link SubagentRequiredCaps} — the SSOT that mirrors
571
+ * `resolveSubagentLifecycle` in the handler. It folds in `shutdown` and
572
+ * `init` (including the handler's auto-injected `"pause"` /
573
+ * `"pause-until-parent-close"` overrides), so any `(adapter, source,
574
+ * continuation, init, shutdown)` cell that can't execute at runtime
575
+ * fails to typecheck at the `defineSubagent` site.
576
+ *
577
+ * `TOptions` defaults to {@link SandboxCreateOptions} so the wide,
578
+ * un-parameterised `SubagentSandboxConfig` keeps working for callers
579
+ * that don't need adapter-specific options.
100
580
  */
101
- export type SubagentSandboxConfig =
102
- | "none"
103
- | {
104
- source: "inherit";
105
- continuation: "continue" | "fork";
106
- shutdown?: SubagentSandboxShutdown;
107
- proxy: (scope: string) => SandboxOps;
108
- }
109
- | {
110
- source: "own";
111
- init?: "per-call" | "once";
112
- continuation: "continue" | "fork" | "snapshot";
113
- shutdown?: SubagentSandboxShutdown;
114
- proxy: (scope: string) => SandboxOps;
115
- };
581
+ export type SubagentSandboxConfig<
582
+ TOptions extends SandboxCreateOptions = SandboxCreateOptions,
583
+ > = "none" | InheritVariant<TOptions> | OwnVariant<TOptions>;
116
584
 
117
585
  /**
118
586
  * Configuration for a subagent that can be spawned by the parent workflow.
@@ -278,5 +746,13 @@ export interface SubagentSessionInput {
278
746
  sandboxId?: string;
279
747
  snapshot?: SandboxSnapshot;
280
748
  threadId: string;
749
+ usage: {
750
+ totalInputTokens: number;
751
+ totalOutputTokens: number;
752
+ totalCachedWriteTokens: number;
753
+ totalCachedReadTokens: number;
754
+ totalReasonTokens: number;
755
+ turns: number;
756
+ };
281
757
  }) => void;
282
758
  }
@@ -13,6 +13,7 @@ import type {
13
13
  } from "./types";
14
14
  import type { SubagentSandboxShutdown } from "../lifecycle";
15
15
  import type { SandboxSnapshot } from "../sandbox/types";
16
+ import type { TokenUsage } from "../types";
16
17
  import { childSandboxReadySignal } from "./signals";
17
18
 
18
19
  /**
@@ -132,6 +133,7 @@ export function defineSubagentWorkflow(
132
133
  let capturedSnapshot: SandboxSnapshot | undefined;
133
134
  let capturedBaseSnapshot: SandboxSnapshot | undefined;
134
135
  let capturedThreadId: string | undefined;
136
+ let capturedUsage: TokenUsage | undefined;
135
137
  const sessionInput: SubagentSessionInput = {
136
138
  agentName: config.name,
137
139
  sandboxShutdown: effectiveShutdown,
@@ -148,10 +150,17 @@ export function defineSubagentWorkflow(
148
150
  });
149
151
  }
150
152
  },
151
- onSessionExit: ({ sandboxId, snapshot, threadId }) => {
153
+ onSessionExit: ({ sandboxId, snapshot, threadId, usage }) => {
152
154
  capturedSandboxId = sandboxId;
153
155
  capturedSnapshot = snapshot;
154
156
  capturedThreadId = threadId;
157
+ capturedUsage = {
158
+ inputTokens: usage.totalInputTokens,
159
+ outputTokens: usage.totalOutputTokens,
160
+ cachedWriteTokens: usage.totalCachedWriteTokens,
161
+ cachedReadTokens: usage.totalCachedReadTokens,
162
+ reasonTokens: usage.totalReasonTokens,
163
+ };
155
164
  },
156
165
  };
157
166
 
@@ -168,6 +177,7 @@ export function defineSubagentWorkflow(
168
177
  ...(capturedBaseSnapshot !== undefined && {
169
178
  baseSnapshot: capturedBaseSnapshot,
170
179
  }),
180
+ ...(capturedUsage !== undefined && { usage: capturedUsage }),
171
181
  };
172
182
  };
173
183