zeitlich 0.2.36 → 0.2.37

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 (200) hide show
  1. package/README.md +146 -92
  2. package/dist/{activities-BVI2lTwr.d.ts → activities-Bb-nAjwQ.d.ts} +2 -2
  3. package/dist/{activities-hd4aNnZE.d.cts → activities-vkI4_3CC.d.cts} +2 -2
  4. package/dist/adapters/sandbox/bedrock/index.cjs +14 -11
  5. package/dist/adapters/sandbox/bedrock/index.cjs.map +1 -1
  6. package/dist/adapters/sandbox/bedrock/index.d.cts +4 -3
  7. package/dist/adapters/sandbox/bedrock/index.d.ts +4 -3
  8. package/dist/adapters/sandbox/bedrock/index.js +14 -11
  9. package/dist/adapters/sandbox/bedrock/index.js.map +1 -1
  10. package/dist/adapters/sandbox/bedrock/workflow.cjs +2 -0
  11. package/dist/adapters/sandbox/bedrock/workflow.cjs.map +1 -1
  12. package/dist/adapters/sandbox/bedrock/workflow.d.cts +2 -2
  13. package/dist/adapters/sandbox/bedrock/workflow.d.ts +2 -2
  14. package/dist/adapters/sandbox/bedrock/workflow.js +2 -0
  15. package/dist/adapters/sandbox/bedrock/workflow.js.map +1 -1
  16. package/dist/adapters/sandbox/daytona/index.cjs +8 -0
  17. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  18. package/dist/adapters/sandbox/daytona/index.d.cts +2 -1
  19. package/dist/adapters/sandbox/daytona/index.d.ts +2 -1
  20. package/dist/adapters/sandbox/daytona/index.js +8 -0
  21. package/dist/adapters/sandbox/daytona/index.js.map +1 -1
  22. package/dist/adapters/sandbox/daytona/workflow.cjs +2 -0
  23. package/dist/adapters/sandbox/daytona/workflow.cjs.map +1 -1
  24. package/dist/adapters/sandbox/daytona/workflow.d.cts +1 -1
  25. package/dist/adapters/sandbox/daytona/workflow.d.ts +1 -1
  26. package/dist/adapters/sandbox/daytona/workflow.js +2 -0
  27. package/dist/adapters/sandbox/daytona/workflow.js.map +1 -1
  28. package/dist/adapters/sandbox/e2b/index.cjs +59 -10
  29. package/dist/adapters/sandbox/e2b/index.cjs.map +1 -1
  30. package/dist/adapters/sandbox/e2b/index.d.cts +5 -3
  31. package/dist/adapters/sandbox/e2b/index.d.ts +5 -3
  32. package/dist/adapters/sandbox/e2b/index.js +59 -10
  33. package/dist/adapters/sandbox/e2b/index.js.map +1 -1
  34. package/dist/adapters/sandbox/e2b/workflow.cjs +2 -0
  35. package/dist/adapters/sandbox/e2b/workflow.cjs.map +1 -1
  36. package/dist/adapters/sandbox/e2b/workflow.d.cts +1 -1
  37. package/dist/adapters/sandbox/e2b/workflow.d.ts +1 -1
  38. package/dist/adapters/sandbox/e2b/workflow.js +2 -0
  39. package/dist/adapters/sandbox/e2b/workflow.js.map +1 -1
  40. package/dist/adapters/sandbox/inmemory/index.cjs +5 -0
  41. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
  42. package/dist/adapters/sandbox/inmemory/index.d.cts +2 -1
  43. package/dist/adapters/sandbox/inmemory/index.d.ts +2 -1
  44. package/dist/adapters/sandbox/inmemory/index.js +5 -0
  45. package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
  46. package/dist/adapters/sandbox/inmemory/workflow.cjs +2 -0
  47. package/dist/adapters/sandbox/inmemory/workflow.cjs.map +1 -1
  48. package/dist/adapters/sandbox/inmemory/workflow.d.cts +1 -1
  49. package/dist/adapters/sandbox/inmemory/workflow.d.ts +1 -1
  50. package/dist/adapters/sandbox/inmemory/workflow.js +2 -0
  51. package/dist/adapters/sandbox/inmemory/workflow.js.map +1 -1
  52. package/dist/adapters/thread/anthropic/index.cjs +71 -36
  53. package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
  54. package/dist/adapters/thread/anthropic/index.d.cts +5 -5
  55. package/dist/adapters/thread/anthropic/index.d.ts +5 -5
  56. package/dist/adapters/thread/anthropic/index.js +71 -36
  57. package/dist/adapters/thread/anthropic/index.js.map +1 -1
  58. package/dist/adapters/thread/anthropic/workflow.cjs +5 -1
  59. package/dist/adapters/thread/anthropic/workflow.cjs.map +1 -1
  60. package/dist/adapters/thread/anthropic/workflow.d.cts +5 -5
  61. package/dist/adapters/thread/anthropic/workflow.d.ts +5 -5
  62. package/dist/adapters/thread/anthropic/workflow.js +5 -1
  63. package/dist/adapters/thread/anthropic/workflow.js.map +1 -1
  64. package/dist/adapters/thread/google-genai/index.cjs +50 -25
  65. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  66. package/dist/adapters/thread/google-genai/index.d.cts +5 -5
  67. package/dist/adapters/thread/google-genai/index.d.ts +5 -5
  68. package/dist/adapters/thread/google-genai/index.js +50 -25
  69. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  70. package/dist/adapters/thread/google-genai/workflow.cjs +5 -1
  71. package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
  72. package/dist/adapters/thread/google-genai/workflow.d.cts +5 -5
  73. package/dist/adapters/thread/google-genai/workflow.d.ts +5 -5
  74. package/dist/adapters/thread/google-genai/workflow.js +5 -1
  75. package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
  76. package/dist/adapters/thread/langchain/index.cjs +34 -7
  77. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  78. package/dist/adapters/thread/langchain/index.d.cts +5 -5
  79. package/dist/adapters/thread/langchain/index.d.ts +5 -5
  80. package/dist/adapters/thread/langchain/index.js +34 -7
  81. package/dist/adapters/thread/langchain/index.js.map +1 -1
  82. package/dist/adapters/thread/langchain/workflow.cjs +5 -1
  83. package/dist/adapters/thread/langchain/workflow.cjs.map +1 -1
  84. package/dist/adapters/thread/langchain/workflow.d.cts +5 -5
  85. package/dist/adapters/thread/langchain/workflow.d.ts +5 -5
  86. package/dist/adapters/thread/langchain/workflow.js +5 -1
  87. package/dist/adapters/thread/langchain/workflow.js.map +1 -1
  88. package/dist/index.cjs +206 -120
  89. package/dist/index.cjs.map +1 -1
  90. package/dist/index.d.cts +17 -11
  91. package/dist/index.d.ts +17 -11
  92. package/dist/index.js +207 -121
  93. package/dist/index.js.map +1 -1
  94. package/dist/{proxy-BjdFGPTm.d.ts → proxy-0smGKvx8.d.ts} +1 -1
  95. package/dist/{proxy-7RnVaPdJ.d.cts → proxy-DEtowJyd.d.cts} +1 -1
  96. package/dist/{thread-manager-DjN5JYul.d.ts → thread-manager-3fszQih4.d.ts} +2 -2
  97. package/dist/{thread-manager-CbpiGq1L.d.ts → thread-manager-C-C4pI2z.d.ts} +2 -2
  98. package/dist/{thread-manager-BBzNgQWH.d.cts → thread-manager-CzYln2OC.d.cts} +2 -2
  99. package/dist/{thread-manager-DzXm9eeI.d.cts → thread-manager-D4vgzYrh.d.cts} +2 -2
  100. package/dist/{types-yiXmqedU.d.ts → types-B37hKoWA.d.ts} +1 -1
  101. package/dist/{types-DQ1l_gXL.d.cts → types-BO7Yju20.d.cts} +63 -14
  102. package/dist/{types-wiGLvxWf.d.ts → types-CNuWnvy9.d.ts} +1 -1
  103. package/dist/{types-CADc5V_P.d.ts → types-CPKDl-y_.d.ts} +63 -14
  104. package/dist/{types-Mc_4BCfT.d.cts → types-D08CXPh8.d.cts} +1 -1
  105. package/dist/{types-CBH54cwr.d.cts → types-DWEUmYAJ.d.cts} +1 -1
  106. package/dist/{types-DxCpFNv_.d.cts → types-tQL9njTu.d.cts} +25 -0
  107. package/dist/{types-DxCpFNv_.d.ts → types-tQL9njTu.d.ts} +25 -0
  108. package/dist/{workflow-P2pTSfKu.d.ts → workflow-CjXHbZZc.d.ts} +2 -2
  109. package/dist/{workflow-DhtWRovz.d.cts → workflow-Do_lzJpT.d.cts} +2 -2
  110. package/dist/workflow.cjs +182 -114
  111. package/dist/workflow.cjs.map +1 -1
  112. package/dist/workflow.d.cts +3 -3
  113. package/dist/workflow.d.ts +3 -3
  114. package/dist/workflow.js +183 -115
  115. package/dist/workflow.js.map +1 -1
  116. package/package.json +1 -1
  117. package/src/adapters/sandbox/bedrock/filesystem.ts +6 -12
  118. package/src/adapters/sandbox/bedrock/index.ts +10 -8
  119. package/src/adapters/sandbox/bedrock/proxy.ts +2 -0
  120. package/src/adapters/sandbox/daytona/index.ts +6 -0
  121. package/src/adapters/sandbox/daytona/proxy.ts +2 -0
  122. package/src/adapters/sandbox/e2b/filesystem.ts +5 -4
  123. package/src/adapters/sandbox/e2b/index.ts +63 -12
  124. package/src/adapters/sandbox/e2b/proxy.ts +2 -0
  125. package/src/adapters/sandbox/inmemory/index.ts +5 -0
  126. package/src/adapters/sandbox/inmemory/proxy.ts +2 -0
  127. package/src/adapters/thread/anthropic/activities.ts +49 -26
  128. package/src/adapters/thread/anthropic/model-invoker.ts +15 -6
  129. package/src/adapters/thread/anthropic/proxy.ts +6 -2
  130. package/src/adapters/thread/anthropic/thread-manager.test.ts +26 -7
  131. package/src/adapters/thread/anthropic/thread-manager.ts +60 -46
  132. package/src/adapters/thread/google-genai/activities.ts +7 -2
  133. package/src/adapters/thread/google-genai/model-invoker.ts +26 -8
  134. package/src/adapters/thread/google-genai/proxy.ts +6 -2
  135. package/src/adapters/thread/google-genai/thread-manager.test.ts +13 -3
  136. package/src/adapters/thread/google-genai/thread-manager.ts +54 -33
  137. package/src/adapters/thread/langchain/activities.ts +46 -24
  138. package/src/adapters/thread/langchain/hooks.test.ts +36 -49
  139. package/src/adapters/thread/langchain/hooks.ts +18 -5
  140. package/src/adapters/thread/langchain/model-invoker.ts +3 -3
  141. package/src/adapters/thread/langchain/proxy.ts +6 -2
  142. package/src/adapters/thread/langchain/thread-manager.test.ts +5 -1
  143. package/src/adapters/thread/langchain/thread-manager.ts +20 -9
  144. package/src/index.ts +4 -1
  145. package/src/lib/activity.ts +16 -6
  146. package/src/lib/hooks/types.ts +6 -6
  147. package/src/lib/lifecycle.ts +9 -1
  148. package/src/lib/model/proxy.ts +2 -2
  149. package/src/lib/observability/hooks.ts +4 -5
  150. package/src/lib/observability/index.ts +1 -4
  151. package/src/lib/sandbox/manager.ts +21 -4
  152. package/src/lib/sandbox/node-fs.ts +3 -6
  153. package/src/lib/sandbox/sandbox.test.ts +36 -3
  154. package/src/lib/sandbox/tree.integration.test.ts +10 -3
  155. package/src/lib/sandbox/types.ts +35 -1
  156. package/src/lib/session/session-edge-cases.integration.test.ts +51 -13
  157. package/src/lib/session/session.integration.test.ts +139 -0
  158. package/src/lib/session/session.ts +50 -19
  159. package/src/lib/session/types.ts +13 -5
  160. package/src/lib/skills/fs-provider.ts +12 -8
  161. package/src/lib/skills/handler.ts +1 -1
  162. package/src/lib/skills/parse.ts +3 -1
  163. package/src/lib/skills/register.ts +1 -3
  164. package/src/lib/skills/skills.integration.test.ts +25 -15
  165. package/src/lib/state/manager.integration.test.ts +12 -2
  166. package/src/lib/subagent/define.ts +1 -1
  167. package/src/lib/subagent/handler.ts +186 -71
  168. package/src/lib/subagent/index.ts +1 -5
  169. package/src/lib/subagent/register.ts +3 -2
  170. package/src/lib/subagent/signals.ts +1 -10
  171. package/src/lib/subagent/subagent.integration.test.ts +438 -156
  172. package/src/lib/subagent/tool.ts +4 -3
  173. package/src/lib/subagent/types.ts +50 -20
  174. package/src/lib/subagent/workflow.ts +9 -49
  175. package/src/lib/thread/id.test.ts +1 -1
  176. package/src/lib/thread/id.ts +1 -2
  177. package/src/lib/thread/proxy.ts +3 -4
  178. package/src/lib/thread/types.ts +11 -3
  179. package/src/lib/tool-router/index.ts +1 -5
  180. package/src/lib/tool-router/router-edge-cases.integration.test.ts +1 -1
  181. package/src/lib/tool-router/router.ts +3 -2
  182. package/src/lib/tool-router/types.ts +11 -3
  183. package/src/lib/tool-router/with-sandbox.ts +19 -5
  184. package/src/lib/virtual-fs/filesystem.ts +1 -1
  185. package/src/lib/virtual-fs/index.ts +5 -1
  186. package/src/lib/virtual-fs/mutations.ts +2 -4
  187. package/src/lib/virtual-fs/queries.ts +9 -5
  188. package/src/lib/virtual-fs/types.ts +4 -1
  189. package/src/lib/virtual-fs/virtual-fs.test.ts +9 -11
  190. package/src/lib/workflow.test.ts +7 -4
  191. package/src/lib/workflow.ts +1 -5
  192. package/src/tools/ask-user-question/tool.ts +1 -3
  193. package/src/tools/glob/handler.ts +1 -4
  194. package/src/tools/task-get/handler.ts +4 -5
  195. package/src/tools/task-list/handler.ts +1 -4
  196. package/src/tools/task-update/handler.ts +4 -5
  197. package/src/workflow.ts +20 -7
  198. package/tsup.config.ts +9 -6
  199. package/src/lib/.env +0 -1
  200. package/src/tools/bash/.env +0 -1
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { defineSignal, uuid4, setHandler, defineUpdate, ApplicationFailure, log, condition, defineQuery, proxySinks, workflowInfo, proxyActivities, getExternalWorkflowHandle, startChild } from '@temporalio/workflow';
1
+ import { defineSignal, uuid4, setHandler, defineUpdate, ApplicationFailure, log, condition, defineQuery, proxySinks, workflowInfo, proxyActivities, getExternalWorkflowHandle, executeChild } from '@temporalio/workflow';
2
2
  import z14, { z } from 'zod';
3
3
  import { randomUUID, randomFillSync } from 'crypto';
4
4
  import { ApplicationFailure as ApplicationFailure$1 } from '@temporalio/common';
@@ -389,9 +389,7 @@ function createSubagentTool(subagents) {
389
389
  schema
390
390
  };
391
391
  }
392
- var childResultSignal = defineSignal("childResult");
393
392
  var childSandboxReadySignal = defineSignal("childSandboxReady");
394
- var destroySandboxSignal = defineSignal("destroySandbox");
395
393
 
396
394
  // src/lib/subagent/handler.ts
397
395
  function resolveSandboxConfig(config) {
@@ -415,25 +413,27 @@ function resolveSandboxConfig(config) {
415
413
  }
416
414
  function createSubagentHandler(subagents) {
417
415
  const { taskQueue: parentTaskQueue } = workflowInfo();
418
- const childResults = /* @__PURE__ */ new Map();
416
+ const agentSandboxOps = /* @__PURE__ */ new Map();
417
+ for (const cfg of subagents) {
418
+ if (cfg.sandbox && cfg.sandbox !== "none") {
419
+ agentSandboxOps.set(cfg.agentName, cfg.sandbox.proxy(cfg.agentName));
420
+ }
421
+ }
419
422
  const pendingDestroys = /* @__PURE__ */ new Map();
420
423
  const threadSandboxes = /* @__PURE__ */ new Map();
421
424
  const persistentSandboxes = /* @__PURE__ */ new Map();
422
425
  const persistentSandboxCreating = /* @__PURE__ */ new Set();
423
426
  const lazyCreatorAgent = /* @__PURE__ */ new Map();
424
- setHandler(childResultSignal, ({ childWorkflowId, result }) => {
425
- childResults.set(childWorkflowId, result);
426
- });
427
- setHandler(
428
- childSandboxReadySignal,
429
- ({ childWorkflowId, sandboxId }) => {
430
- const agentName = lazyCreatorAgent.get(childWorkflowId);
431
- if (agentName && !persistentSandboxes.has(agentName)) {
432
- persistentSandboxes.set(agentName, sandboxId);
433
- lazyCreatorAgent.delete(childWorkflowId);
434
- }
427
+ const threadSnapshots = /* @__PURE__ */ new Map();
428
+ const persistentBaseSnapshot = /* @__PURE__ */ new Map();
429
+ const persistentBaseSnapshotCreating = /* @__PURE__ */ new Set();
430
+ setHandler(childSandboxReadySignal, ({ childWorkflowId, sandboxId }) => {
431
+ const agentName = lazyCreatorAgent.get(childWorkflowId);
432
+ if (agentName && !persistentSandboxes.has(agentName)) {
433
+ persistentSandboxes.set(agentName, sandboxId);
434
+ lazyCreatorAgent.delete(childWorkflowId);
435
435
  }
436
- );
436
+ });
437
437
  const handler = async (args, context) => {
438
438
  const config = subagents.find((s) => s.agentName === args.subagent);
439
439
  if (!config) {
@@ -444,6 +444,12 @@ function createSubagentHandler(subagents) {
444
444
  const childWorkflowId = `${args.subagent}-${getShortId()}`;
445
445
  const { sandboxId: parentSandboxId } = context;
446
446
  const sandboxCfg = resolveSandboxConfig(config.sandbox);
447
+ if (sandboxCfg.source !== "none" && !agentSandboxOps.has(config.agentName)) {
448
+ throw ApplicationFailure.create({
449
+ message: `Subagent "${config.agentName}" uses a sandbox but no \`sandbox.proxy\` is configured on its SubagentConfig`,
450
+ nonRetryable: true
451
+ });
452
+ }
447
453
  if (sandboxCfg.source === "inherit" && !parentSandboxId) {
448
454
  throw new Error(
449
455
  `Subagent "${config.agentName}" is configured with sandbox: "inherit" but the parent has no sandbox`
@@ -462,12 +468,39 @@ function createSubagentHandler(subagents) {
462
468
  let sandbox;
463
469
  let sandboxShutdownOverride;
464
470
  let isLazyCreator = false;
471
+ let isSnapshotBaseCreator = false;
465
472
  if (sandboxCfg.source === "inherit" && parentSandboxId) {
466
473
  if (sandboxCfg.continuation === "fork") {
467
474
  sandbox = { mode: "fork", sandboxId: parentSandboxId };
475
+ } else if (sandboxCfg.continuation === "snapshot") {
476
+ throw new Error(
477
+ `Subagent "${config.agentName}" has sandbox source "inherit" with continuation "snapshot" \u2014 snapshot continuation is only supported for source "own"`
478
+ );
468
479
  } else {
469
480
  sandbox = { mode: "inherit", sandboxId: parentSandboxId };
470
481
  }
482
+ } else if (sandboxCfg.source === "own" && sandboxCfg.continuation === "snapshot") {
483
+ const isLazy = sandboxCfg.init === "once";
484
+ let baseSnap;
485
+ if (continuationThreadId) {
486
+ baseSnap = threadSnapshots.get(continuationThreadId)?.snapshot;
487
+ }
488
+ if (!baseSnap && isLazy) {
489
+ baseSnap = persistentBaseSnapshot.get(config.agentName);
490
+ if (!baseSnap) {
491
+ if (persistentBaseSnapshotCreating.has(config.agentName)) {
492
+ await condition(() => persistentBaseSnapshot.has(config.agentName));
493
+ baseSnap = persistentBaseSnapshot.get(config.agentName);
494
+ } else {
495
+ persistentBaseSnapshotCreating.add(config.agentName);
496
+ isSnapshotBaseCreator = true;
497
+ }
498
+ }
499
+ }
500
+ if (baseSnap) {
501
+ sandbox = { mode: "from-snapshot", snapshot: baseSnap };
502
+ }
503
+ sandboxShutdownOverride = "snapshot";
471
504
  } else if (sandboxCfg.source === "own") {
472
505
  const isLazy = sandboxCfg.init === "once";
473
506
  let baseSandboxId;
@@ -518,31 +551,8 @@ function createSubagentHandler(subagents) {
518
551
  threadMode,
519
552
  sandboxSource: sandboxCfg.source
520
553
  });
521
- const childHandle = await startChild(config.workflow, childOpts);
554
+ const childResult = await executeChild(config.workflow, childOpts);
522
555
  const effectiveShutdown = sandboxShutdownOverride ?? sandboxCfg.shutdown ?? "destroy";
523
- if (effectiveShutdown === "pause-until-parent-close" || effectiveShutdown === "keep-until-parent-close") {
524
- const key = isLazyCreator ? `persistent:${config.agentName}` : childWorkflowId;
525
- pendingDestroys.set(key, childHandle);
526
- }
527
- await Promise.race([
528
- condition(() => childResults.has(childWorkflowId)),
529
- childHandle.result()
530
- ]);
531
- if (!childResults.has(childWorkflowId)) {
532
- await condition(() => childResults.has(childWorkflowId));
533
- }
534
- const childResult = childResults.get(childWorkflowId);
535
- childResults.delete(childWorkflowId);
536
- if (!childResult) {
537
- log.warn("subagent returned no result", {
538
- subagent: config.agentName,
539
- childWorkflowId
540
- });
541
- return {
542
- toolResponse: "Subagent workflow did not signal a result",
543
- data: null
544
- };
545
- }
546
556
  log.info("subagent completed", {
547
557
  subagent: config.agentName,
548
558
  childWorkflowId,
@@ -554,19 +564,42 @@ function createSubagentHandler(subagents) {
554
564
  usage,
555
565
  threadId: childThreadId,
556
566
  sandboxId: childSandboxId,
567
+ snapshot: childSnapshot,
568
+ baseSnapshot: childBaseSnapshot,
557
569
  metadata
558
570
  } = childResult;
559
571
  if (childSandboxId) {
560
- if (sandboxCfg.source === "own" && sandboxCfg.init === "once" && !persistentSandboxes.has(config.agentName)) {
572
+ if (sandboxCfg.source === "own" && sandboxCfg.init === "once" && sandboxCfg.continuation !== "snapshot" && !persistentSandboxes.has(config.agentName)) {
561
573
  persistentSandboxes.set(config.agentName, childSandboxId);
562
- } else if (allowsContinuation && childThreadId) {
574
+ } else if (allowsContinuation && childThreadId && sandboxCfg.source === "own" && sandboxCfg.continuation !== "snapshot") {
563
575
  threadSandboxes.set(childThreadId, childSandboxId);
564
576
  }
565
577
  }
578
+ if (childSandboxId && (effectiveShutdown === "pause-until-parent-close" || effectiveShutdown === "keep-until-parent-close")) {
579
+ const key = isLazyCreator ? `persistent:${config.agentName}` : childWorkflowId;
580
+ pendingDestroys.set(key, {
581
+ agentName: config.agentName,
582
+ sandboxId: childSandboxId
583
+ });
584
+ }
585
+ if (sandboxCfg.source === "own" && sandboxCfg.continuation === "snapshot") {
586
+ if (childSnapshot && childThreadId) {
587
+ threadSnapshots.set(childThreadId, {
588
+ agentName: config.agentName,
589
+ snapshot: childSnapshot
590
+ });
591
+ }
592
+ if (isSnapshotBaseCreator && childBaseSnapshot && !persistentBaseSnapshot.has(config.agentName)) {
593
+ persistentBaseSnapshot.set(config.agentName, childBaseSnapshot);
594
+ }
595
+ }
566
596
  if (isLazyCreator) {
567
597
  persistentSandboxCreating.delete(config.agentName);
568
598
  lazyCreatorAgent.delete(childWorkflowId);
569
599
  }
600
+ if (isSnapshotBaseCreator) {
601
+ persistentBaseSnapshotCreating.delete(config.agentName);
602
+ }
570
603
  if (!toolResponse) {
571
604
  return {
572
605
  toolResponse: "Subagent workflow returned no response",
@@ -602,22 +635,60 @@ function createSubagentHandler(subagents) {
602
635
  };
603
636
  };
604
637
  const destroySubagentSandboxes = async () => {
605
- const handles = [...pendingDestroys.values()];
638
+ const entries = [...pendingDestroys.values()];
606
639
  pendingDestroys.clear();
607
640
  await Promise.all(
608
- handles.map(async (handle) => {
641
+ entries.map(async ({ agentName, sandboxId }) => {
642
+ const ops = agentSandboxOps.get(agentName);
643
+ if (!ops) {
644
+ log.warn(
645
+ "Skipping sandbox destroy \u2014 no sandbox.proxy registered for agent",
646
+ { agentName, sandboxId }
647
+ );
648
+ return;
649
+ }
650
+ try {
651
+ await ops.destroySandbox(sandboxId);
652
+ } catch (err) {
653
+ log.warn("Failed to destroy subagent sandbox", {
654
+ agentName,
655
+ sandboxId,
656
+ error: err
657
+ });
658
+ }
659
+ })
660
+ );
661
+ };
662
+ const cleanupSubagentSnapshots = async () => {
663
+ const tagged = [];
664
+ for (const entry of threadSnapshots.values()) tagged.push(entry);
665
+ for (const [agentName, snapshot] of persistentBaseSnapshot.entries()) {
666
+ tagged.push({ agentName, snapshot });
667
+ }
668
+ threadSnapshots.clear();
669
+ persistentBaseSnapshot.clear();
670
+ await Promise.all(
671
+ tagged.map(async ({ agentName, snapshot }) => {
672
+ const ops = agentSandboxOps.get(agentName);
673
+ if (!ops) {
674
+ log.warn(
675
+ "Skipping snapshot delete \u2014 no sandbox.proxy registered for agent",
676
+ { agentName }
677
+ );
678
+ return;
679
+ }
609
680
  try {
610
- await handle.signal(destroySandboxSignal);
611
- await handle.result();
681
+ await ops.deleteSandboxSnapshot(snapshot);
612
682
  } catch (err) {
613
- log.warn("Failed to signal destroySandbox to child workflow", {
683
+ log.warn("Failed to delete subagent snapshot", {
684
+ agentName,
614
685
  error: err
615
686
  });
616
687
  }
617
688
  })
618
689
  );
619
690
  };
620
- return { handler, destroySubagentSandboxes };
691
+ return { handler, destroySubagentSandboxes, cleanupSubagentSnapshots };
621
692
  }
622
693
 
623
694
  // src/lib/subagent/register.ts
@@ -631,7 +702,7 @@ function buildSubagentRegistration(subagents) {
631
702
  if (s.hooks) subagentHooksMap.set(s.agentName, s.hooks);
632
703
  }
633
704
  const resolveSubagentName = (args) => args.subagent;
634
- const { handler, destroySubagentSandboxes } = createSubagentHandler(subagents);
705
+ const { handler, destroySubagentSandboxes, cleanupSubagentSnapshots } = createSubagentHandler(subagents);
635
706
  const registration = {
636
707
  name: SUBAGENT_TOOL_NAME,
637
708
  enabled: () => getEnabled().length > 0,
@@ -655,7 +726,7 @@ function buildSubagentRegistration(subagents) {
655
726
  }
656
727
  }
657
728
  };
658
- return { registration, destroySubagentSandboxes };
729
+ return { registration, destroySubagentSandboxes, cleanupSubagentSnapshots };
659
730
  }
660
731
  var READ_SKILL_TOOL_NAME = "ReadSkill";
661
732
  function buildReadSkillDescription(skills) {
@@ -728,9 +799,7 @@ function validateSkillNames(skills) {
728
799
  const names = skills.map((s) => s.name);
729
800
  const dupes = names.filter((n, i) => names.indexOf(n) !== i);
730
801
  if (dupes.length > 0) {
731
- throw new Error(
732
- `Duplicate skill names: ${[...new Set(dupes)].join(", ")}`
733
- );
802
+ throw new Error(`Duplicate skill names: ${[...new Set(dupes)].join(", ")}`);
734
803
  }
735
804
  }
736
805
  function buildSkillRegistration(skills) {
@@ -800,11 +869,13 @@ async function createSession({
800
869
  } = threadOps;
801
870
  const plugins = [];
802
871
  let destroySubagentSandboxes;
872
+ let cleanupSubagentSnapshots;
803
873
  if (subagents) {
804
874
  const result = buildSubagentRegistration(subagents);
805
875
  if (result) {
806
876
  plugins.push(result.registration);
807
877
  destroySubagentSandboxes = result.destroySubagentSandboxes;
878
+ cleanupSubagentSnapshots = result.cleanupSubagentSnapshots;
808
879
  }
809
880
  }
810
881
  if (skills) {
@@ -857,6 +928,9 @@ async function createSession({
857
928
  const sandboxMode = sandboxInit?.mode;
858
929
  let sandboxId;
859
930
  let sandboxOwned = false;
931
+ let baseSnapshot;
932
+ let exitSnapshot;
933
+ let freshlyCreated = false;
860
934
  if (sandboxMode === "inherit") {
861
935
  const inheritInit = sandboxInit;
862
936
  sandboxId = inheritInit.sandboxId;
@@ -889,6 +963,16 @@ async function createSession({
889
963
  sandboxInit.sandboxId
890
964
  );
891
965
  sandboxOwned = true;
966
+ } else if (sandboxMode === "from-snapshot") {
967
+ if (!sandboxOps) {
968
+ throw ApplicationFailure.create({
969
+ message: "No sandboxOps provided \u2014 cannot restore sandbox",
970
+ nonRetryable: true
971
+ });
972
+ }
973
+ const snap = sandboxInit.snapshot;
974
+ sandboxId = await sandboxOps.restoreSandbox(snap);
975
+ sandboxOwned = true;
892
976
  } else if (sandboxOps) {
893
977
  const skillFiles = skills ? collectSkillFiles(skills) : void 0;
894
978
  const ctx = sandboxInit?.ctx;
@@ -897,9 +981,13 @@ async function createSession({
897
981
  if (result) {
898
982
  sandboxId = result.sandboxId;
899
983
  sandboxOwned = true;
984
+ freshlyCreated = true;
900
985
  }
901
986
  }
902
- if (sandboxId && onSandboxReady) {
987
+ if (sandboxId && sandboxOwned && freshlyCreated && sandboxShutdown === "snapshot" && sandboxOps) {
988
+ baseSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
989
+ }
990
+ if (sandboxId && sandboxOwned && onSandboxReady) {
903
991
  onSandboxReady(sandboxId);
904
992
  }
905
993
  if (virtualFsConfig) {
@@ -965,6 +1053,7 @@ async function createSession({
965
1053
  threadKey
966
1054
  );
967
1055
  let exitReason = "completed";
1056
+ let finalMessage = null;
968
1057
  try {
969
1058
  while (stateManager.isRunning() && !stateManager.isTerminal() && stateManager.getTurns() < maxTurns) {
970
1059
  stateManager.incrementTurns();
@@ -991,21 +1080,8 @@ async function createSession({
991
1080
  if (!toolRouter.hasTools() || rawToolCalls.length === 0) {
992
1081
  stateManager.complete();
993
1082
  exitReason = "completed";
994
- log.info("session ended", {
995
- agentName,
996
- threadId,
997
- exitReason,
998
- turns: currentTurn,
999
- durationMs: Date.now() - sessionStartMs,
1000
- usage: stateManager.getTotalUsage()
1001
- });
1002
- return {
1003
- threadId,
1004
- finalMessage: message,
1005
- exitReason,
1006
- usage: stateManager.getTotalUsage(),
1007
- sandboxId
1008
- };
1083
+ finalMessage = message;
1084
+ break;
1009
1085
  }
1010
1086
  const parsedToolCalls = [];
1011
1087
  for (const tc of rawToolCalls) {
@@ -1077,11 +1153,21 @@ async function createSession({
1077
1153
  case "pause-until-parent-close":
1078
1154
  await sandboxOps.pauseSandbox(sandboxId);
1079
1155
  break;
1156
+ case "keep":
1157
+ case "keep-until-parent-close":
1158
+ break;
1159
+ case "snapshot":
1160
+ exitSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
1161
+ await sandboxOps.destroySandbox(sandboxId);
1162
+ break;
1080
1163
  }
1081
1164
  }
1082
1165
  if (destroySubagentSandboxes) {
1083
1166
  await destroySubagentSandboxes();
1084
1167
  }
1168
+ if (cleanupSubagentSnapshots) {
1169
+ await cleanupSubagentSnapshots();
1170
+ }
1085
1171
  }
1086
1172
  log.info("session ended", {
1087
1173
  agentName,
@@ -1089,14 +1175,18 @@ async function createSession({
1089
1175
  exitReason,
1090
1176
  turns: stateManager.getTurns(),
1091
1177
  durationMs: Date.now() - sessionStartMs,
1092
- usage: stateManager.getTotalUsage()
1178
+ usage: stateManager.getTotalUsage(),
1179
+ ...baseSnapshot && { hasBaseSnapshot: true },
1180
+ ...exitSnapshot && { hasExitSnapshot: true }
1093
1181
  });
1094
1182
  return {
1095
1183
  threadId,
1096
- finalMessage: null,
1184
+ finalMessage,
1097
1185
  exitReason,
1098
1186
  usage: stateManager.getTotalUsage(),
1099
- sandboxId
1187
+ sandboxId,
1188
+ ...baseSnapshot && { baseSnapshot },
1189
+ ...exitSnapshot && { snapshot: exitSnapshot }
1100
1190
  };
1101
1191
  }
1102
1192
  };
@@ -1385,44 +1475,16 @@ function defineSubagentWorkflow(config, fn) {
1385
1475
  ...workflowInput.thread && { thread: workflowInput.thread },
1386
1476
  ...workflowInput.sandbox && { sandbox: workflowInput.sandbox },
1387
1477
  onSandboxReady: (sandboxId) => {
1388
- void parentHandle.signal(childSandboxReadySignal, {
1389
- childWorkflowId: workflowInfo().workflowId,
1390
- sandboxId
1391
- });
1478
+ const isReuse = workflowInput.sandbox?.mode === "continue";
1479
+ if (!isReuse) {
1480
+ void parentHandle.signal(childSandboxReadySignal, {
1481
+ childWorkflowId: workflowInfo().workflowId,
1482
+ sandboxId
1483
+ });
1484
+ }
1392
1485
  }
1393
1486
  };
1394
- const { destroySandbox, ...result } = await fn(
1395
- prompt,
1396
- sessionInput,
1397
- context ?? {}
1398
- );
1399
- if (effectiveShutdown === "pause-until-parent-close" || effectiveShutdown === "keep-until-parent-close") {
1400
- if (!destroySandbox) {
1401
- throw ApplicationFailure.create({
1402
- message: `Subagent "${config.name}" has sandboxShutdown="${effectiveShutdown}" but fn did not return a destroySandbox callback`,
1403
- nonRetryable: true
1404
- });
1405
- }
1406
- if (!result.sandboxId) {
1407
- throw ApplicationFailure.create({
1408
- message: `Subagent "${config.name}" has sandboxShutdown="${effectiveShutdown}" but fn did not return a sandboxId`,
1409
- nonRetryable: true
1410
- });
1411
- }
1412
- }
1413
- await parentHandle.signal(childResultSignal, {
1414
- childWorkflowId: workflowInfo().workflowId,
1415
- result
1416
- });
1417
- if (destroySandbox) {
1418
- let destroyRequested = false;
1419
- setHandler(destroySandboxSignal, () => {
1420
- destroyRequested = true;
1421
- });
1422
- await condition(() => destroyRequested);
1423
- await destroySandbox();
1424
- }
1425
- return result;
1487
+ return fn(prompt, sessionInput, context ?? {});
1426
1488
  };
1427
1489
  Object.defineProperty(workflow, "name", { value: config.name });
1428
1490
  return Object.assign(workflow, {
@@ -1532,9 +1594,7 @@ function applyVirtualTreeMutations(stateManager, mutations) {
1532
1594
  tree = tree.filter((e) => e.path !== m.path);
1533
1595
  break;
1534
1596
  case "update":
1535
- tree = tree.map(
1536
- (e) => e.path === m.path ? { ...e, ...m.entry } : e
1537
- );
1597
+ tree = tree.map((e) => e.path === m.path ? { ...e, ...m.entry } : e);
1538
1598
  break;
1539
1599
  }
1540
1600
  }
@@ -1593,7 +1653,9 @@ function formatVirtualFileTree(entries, opts = {}) {
1593
1653
  // src/lib/virtual-fs/queries.ts
1594
1654
  function hasFileWithMimeType(stateManager, pattern) {
1595
1655
  const tree = stateManager.get("fileTree");
1596
- const matchers = (Array.isArray(pattern) ? pattern : [pattern]).map(buildMatcher);
1656
+ const matchers = (Array.isArray(pattern) ? pattern : [pattern]).map(
1657
+ buildMatcher
1658
+ );
1597
1659
  return tree.some((entry) => {
1598
1660
  const meta = entry.metadata;
1599
1661
  const mime = meta?.mimeType;
@@ -1654,7 +1716,9 @@ function proxyVirtualFsOps(scope, options) {
1654
1716
  // src/lib/skills/parse.ts
1655
1717
  function parseSkillFile(raw) {
1656
1718
  const trimmed = raw.replace(/^\uFEFF/, "");
1657
- const match = trimmed.match(/^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*\r?\n?([\s\S]*)$/);
1719
+ const match = trimmed.match(
1720
+ /^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*\r?\n?([\s\S]*)$/
1721
+ );
1658
1722
  if (!match) {
1659
1723
  throw new Error(
1660
1724
  "SKILL.md must start with YAML frontmatter delimited by ---"
@@ -1926,7 +1990,9 @@ function createTaskGetHandler(stateManager) {
1926
1990
  const task = stateManager.getTask(args.taskId) ?? null;
1927
1991
  if (!task) {
1928
1992
  return {
1929
- toolResponse: JSON.stringify({ error: `Task not found: ${args.taskId}` }),
1993
+ toolResponse: JSON.stringify({
1994
+ error: `Task not found: ${args.taskId}`
1995
+ }),
1930
1996
  data: null
1931
1997
  };
1932
1998
  }
@@ -1969,7 +2035,9 @@ function createTaskUpdateHandler(stateManager) {
1969
2035
  const task = stateManager.getTask(args.taskId);
1970
2036
  if (!task) {
1971
2037
  return {
1972
- toolResponse: JSON.stringify({ error: `Task not found: ${args.taskId}` }),
2038
+ toolResponse: JSON.stringify({
2039
+ error: `Task not found: ${args.taskId}`
2040
+ }),
1973
2041
  data: null
1974
2042
  };
1975
2043
  }
@@ -2079,6 +2147,8 @@ var FileSystemSkillProvider = class {
2079
2147
  this.fs = fs;
2080
2148
  this.baseDir = baseDir;
2081
2149
  }
2150
+ fs;
2151
+ baseDir;
2082
2152
  async listSkills() {
2083
2153
  const dirs = await this.discoverSkillDirs();
2084
2154
  const skills = [];
@@ -2094,9 +2164,7 @@ var FileSystemSkillProvider = class {
2094
2164
  return skills;
2095
2165
  }
2096
2166
  async getSkill(name) {
2097
- const raw = await this.fs.readFile(
2098
- join(this.baseDir, name, "SKILL.md")
2099
- );
2167
+ const raw = await this.fs.readFile(join(this.baseDir, name, "SKILL.md"));
2100
2168
  const { frontmatter, body } = parseSkillFile(raw);
2101
2169
  if (frontmatter.name !== name) {
2102
2170
  throw new Error(
@@ -2105,7 +2173,10 @@ var FileSystemSkillProvider = class {
2105
2173
  }
2106
2174
  const location = join(this.baseDir, name);
2107
2175
  const resourcePaths = await this.discoverResources(name);
2108
- const resourceContents = await this.readResourceContents(location, resourcePaths);
2176
+ const resourceContents = await this.readResourceContents(
2177
+ location,
2178
+ resourcePaths
2179
+ );
2109
2180
  return {
2110
2181
  ...frontmatter,
2111
2182
  instructions: body,
@@ -2125,7 +2196,10 @@ var FileSystemSkillProvider = class {
2125
2196
  const { frontmatter, body } = parseSkillFile(raw);
2126
2197
  const location = join(this.baseDir, dir);
2127
2198
  const resourcePaths = await this.discoverResources(dir);
2128
- const resourceContents = await this.readResourceContents(location, resourcePaths);
2199
+ const resourceContents = await this.readResourceContents(
2200
+ location,
2201
+ resourcePaths
2202
+ );
2129
2203
  skills.push({
2130
2204
  ...frontmatter,
2131
2205
  instructions: body,
@@ -2299,6 +2373,7 @@ var SandboxManager = class {
2299
2373
  this.provider = provider;
2300
2374
  this.hooks = options?.hooks ?? {};
2301
2375
  }
2376
+ provider;
2302
2377
  hooks;
2303
2378
  async create(options, ctx) {
2304
2379
  let providerOptions = options;
@@ -2327,7 +2402,7 @@ var SandboxManager = class {
2327
2402
  }
2328
2403
  const { sandbox } = await this.provider.create(providerOptions);
2329
2404
  if (this.hooks.onPostCreate) {
2330
- await this.hooks.onPostCreate(sandbox.id);
2405
+ await this.hooks.onPostCreate(sandbox, ctx ?? {});
2331
2406
  }
2332
2407
  return { sandboxId: sandbox.id };
2333
2408
  }
@@ -2350,6 +2425,9 @@ var SandboxManager = class {
2350
2425
  const sandbox = await this.provider.restore(snapshot);
2351
2426
  return sandbox.id;
2352
2427
  }
2428
+ async deleteSnapshot(snapshot) {
2429
+ await this.provider.deleteSnapshot(snapshot);
2430
+ }
2353
2431
  async fork(sandboxId) {
2354
2432
  const sandbox = await this.provider.fork(sandboxId);
2355
2433
  return sandbox.id;
@@ -2392,6 +2470,12 @@ var SandboxManager = class {
2392
2470
  snapshotSandbox: async (sandboxId) => {
2393
2471
  return this.snapshot(sandboxId);
2394
2472
  },
2473
+ restoreSandbox: async (snapshot) => {
2474
+ return this.restore(snapshot);
2475
+ },
2476
+ deleteSandboxSnapshot: async (snapshot) => {
2477
+ await this.deleteSnapshot(snapshot);
2478
+ },
2395
2479
  forkSandbox: async (sandboxId) => {
2396
2480
  return this.fork(sandboxId);
2397
2481
  }
@@ -2507,6 +2591,8 @@ var VirtualFileSystem = class {
2507
2591
  ]) : []
2508
2592
  );
2509
2593
  }
2594
+ resolver;
2595
+ ctx;
2510
2596
  workspaceBase;
2511
2597
  entries;
2512
2598
  directories;