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/workflow.cjs CHANGED
@@ -391,9 +391,7 @@ function createSubagentTool(subagents) {
391
391
  schema
392
392
  };
393
393
  }
394
- var childResultSignal = workflow.defineSignal("childResult");
395
394
  var childSandboxReadySignal = workflow.defineSignal("childSandboxReady");
396
- var destroySandboxSignal = workflow.defineSignal("destroySandbox");
397
395
 
398
396
  // src/lib/subagent/handler.ts
399
397
  function resolveSandboxConfig(config) {
@@ -417,25 +415,27 @@ function resolveSandboxConfig(config) {
417
415
  }
418
416
  function createSubagentHandler(subagents) {
419
417
  const { taskQueue: parentTaskQueue } = workflow.workflowInfo();
420
- const childResults = /* @__PURE__ */ new Map();
418
+ const agentSandboxOps = /* @__PURE__ */ new Map();
419
+ for (const cfg of subagents) {
420
+ if (cfg.sandbox && cfg.sandbox !== "none") {
421
+ agentSandboxOps.set(cfg.agentName, cfg.sandbox.proxy(cfg.agentName));
422
+ }
423
+ }
421
424
  const pendingDestroys = /* @__PURE__ */ new Map();
422
425
  const threadSandboxes = /* @__PURE__ */ new Map();
423
426
  const persistentSandboxes = /* @__PURE__ */ new Map();
424
427
  const persistentSandboxCreating = /* @__PURE__ */ new Set();
425
428
  const lazyCreatorAgent = /* @__PURE__ */ new Map();
426
- workflow.setHandler(childResultSignal, ({ childWorkflowId, result }) => {
427
- childResults.set(childWorkflowId, result);
428
- });
429
- workflow.setHandler(
430
- childSandboxReadySignal,
431
- ({ childWorkflowId, sandboxId }) => {
432
- const agentName = lazyCreatorAgent.get(childWorkflowId);
433
- if (agentName && !persistentSandboxes.has(agentName)) {
434
- persistentSandboxes.set(agentName, sandboxId);
435
- lazyCreatorAgent.delete(childWorkflowId);
436
- }
429
+ const threadSnapshots = /* @__PURE__ */ new Map();
430
+ const persistentBaseSnapshot = /* @__PURE__ */ new Map();
431
+ const persistentBaseSnapshotCreating = /* @__PURE__ */ new Set();
432
+ workflow.setHandler(childSandboxReadySignal, ({ childWorkflowId, sandboxId }) => {
433
+ const agentName = lazyCreatorAgent.get(childWorkflowId);
434
+ if (agentName && !persistentSandboxes.has(agentName)) {
435
+ persistentSandboxes.set(agentName, sandboxId);
436
+ lazyCreatorAgent.delete(childWorkflowId);
437
437
  }
438
- );
438
+ });
439
439
  const handler = async (args, context) => {
440
440
  const config = subagents.find((s) => s.agentName === args.subagent);
441
441
  if (!config) {
@@ -446,6 +446,12 @@ function createSubagentHandler(subagents) {
446
446
  const childWorkflowId = `${args.subagent}-${getShortId()}`;
447
447
  const { sandboxId: parentSandboxId } = context;
448
448
  const sandboxCfg = resolveSandboxConfig(config.sandbox);
449
+ if (sandboxCfg.source !== "none" && !agentSandboxOps.has(config.agentName)) {
450
+ throw workflow.ApplicationFailure.create({
451
+ message: `Subagent "${config.agentName}" uses a sandbox but no \`sandbox.proxy\` is configured on its SubagentConfig`,
452
+ nonRetryable: true
453
+ });
454
+ }
449
455
  if (sandboxCfg.source === "inherit" && !parentSandboxId) {
450
456
  throw new Error(
451
457
  `Subagent "${config.agentName}" is configured with sandbox: "inherit" but the parent has no sandbox`
@@ -464,12 +470,39 @@ function createSubagentHandler(subagents) {
464
470
  let sandbox;
465
471
  let sandboxShutdownOverride;
466
472
  let isLazyCreator = false;
473
+ let isSnapshotBaseCreator = false;
467
474
  if (sandboxCfg.source === "inherit" && parentSandboxId) {
468
475
  if (sandboxCfg.continuation === "fork") {
469
476
  sandbox = { mode: "fork", sandboxId: parentSandboxId };
477
+ } else if (sandboxCfg.continuation === "snapshot") {
478
+ throw new Error(
479
+ `Subagent "${config.agentName}" has sandbox source "inherit" with continuation "snapshot" \u2014 snapshot continuation is only supported for source "own"`
480
+ );
470
481
  } else {
471
482
  sandbox = { mode: "inherit", sandboxId: parentSandboxId };
472
483
  }
484
+ } else if (sandboxCfg.source === "own" && sandboxCfg.continuation === "snapshot") {
485
+ const isLazy = sandboxCfg.init === "once";
486
+ let baseSnap;
487
+ if (continuationThreadId) {
488
+ baseSnap = threadSnapshots.get(continuationThreadId)?.snapshot;
489
+ }
490
+ if (!baseSnap && isLazy) {
491
+ baseSnap = persistentBaseSnapshot.get(config.agentName);
492
+ if (!baseSnap) {
493
+ if (persistentBaseSnapshotCreating.has(config.agentName)) {
494
+ await workflow.condition(() => persistentBaseSnapshot.has(config.agentName));
495
+ baseSnap = persistentBaseSnapshot.get(config.agentName);
496
+ } else {
497
+ persistentBaseSnapshotCreating.add(config.agentName);
498
+ isSnapshotBaseCreator = true;
499
+ }
500
+ }
501
+ }
502
+ if (baseSnap) {
503
+ sandbox = { mode: "from-snapshot", snapshot: baseSnap };
504
+ }
505
+ sandboxShutdownOverride = "snapshot";
473
506
  } else if (sandboxCfg.source === "own") {
474
507
  const isLazy = sandboxCfg.init === "once";
475
508
  let baseSandboxId;
@@ -520,31 +553,8 @@ function createSubagentHandler(subagents) {
520
553
  threadMode,
521
554
  sandboxSource: sandboxCfg.source
522
555
  });
523
- const childHandle = await workflow.startChild(config.workflow, childOpts);
556
+ const childResult = await workflow.executeChild(config.workflow, childOpts);
524
557
  const effectiveShutdown = sandboxShutdownOverride ?? sandboxCfg.shutdown ?? "destroy";
525
- if (effectiveShutdown === "pause-until-parent-close" || effectiveShutdown === "keep-until-parent-close") {
526
- const key = isLazyCreator ? `persistent:${config.agentName}` : childWorkflowId;
527
- pendingDestroys.set(key, childHandle);
528
- }
529
- await Promise.race([
530
- workflow.condition(() => childResults.has(childWorkflowId)),
531
- childHandle.result()
532
- ]);
533
- if (!childResults.has(childWorkflowId)) {
534
- await workflow.condition(() => childResults.has(childWorkflowId));
535
- }
536
- const childResult = childResults.get(childWorkflowId);
537
- childResults.delete(childWorkflowId);
538
- if (!childResult) {
539
- workflow.log.warn("subagent returned no result", {
540
- subagent: config.agentName,
541
- childWorkflowId
542
- });
543
- return {
544
- toolResponse: "Subagent workflow did not signal a result",
545
- data: null
546
- };
547
- }
548
558
  workflow.log.info("subagent completed", {
549
559
  subagent: config.agentName,
550
560
  childWorkflowId,
@@ -556,19 +566,42 @@ function createSubagentHandler(subagents) {
556
566
  usage,
557
567
  threadId: childThreadId,
558
568
  sandboxId: childSandboxId,
569
+ snapshot: childSnapshot,
570
+ baseSnapshot: childBaseSnapshot,
559
571
  metadata
560
572
  } = childResult;
561
573
  if (childSandboxId) {
562
- if (sandboxCfg.source === "own" && sandboxCfg.init === "once" && !persistentSandboxes.has(config.agentName)) {
574
+ if (sandboxCfg.source === "own" && sandboxCfg.init === "once" && sandboxCfg.continuation !== "snapshot" && !persistentSandboxes.has(config.agentName)) {
563
575
  persistentSandboxes.set(config.agentName, childSandboxId);
564
- } else if (allowsContinuation && childThreadId) {
576
+ } else if (allowsContinuation && childThreadId && sandboxCfg.source === "own" && sandboxCfg.continuation !== "snapshot") {
565
577
  threadSandboxes.set(childThreadId, childSandboxId);
566
578
  }
567
579
  }
580
+ if (childSandboxId && (effectiveShutdown === "pause-until-parent-close" || effectiveShutdown === "keep-until-parent-close")) {
581
+ const key = isLazyCreator ? `persistent:${config.agentName}` : childWorkflowId;
582
+ pendingDestroys.set(key, {
583
+ agentName: config.agentName,
584
+ sandboxId: childSandboxId
585
+ });
586
+ }
587
+ if (sandboxCfg.source === "own" && sandboxCfg.continuation === "snapshot") {
588
+ if (childSnapshot && childThreadId) {
589
+ threadSnapshots.set(childThreadId, {
590
+ agentName: config.agentName,
591
+ snapshot: childSnapshot
592
+ });
593
+ }
594
+ if (isSnapshotBaseCreator && childBaseSnapshot && !persistentBaseSnapshot.has(config.agentName)) {
595
+ persistentBaseSnapshot.set(config.agentName, childBaseSnapshot);
596
+ }
597
+ }
568
598
  if (isLazyCreator) {
569
599
  persistentSandboxCreating.delete(config.agentName);
570
600
  lazyCreatorAgent.delete(childWorkflowId);
571
601
  }
602
+ if (isSnapshotBaseCreator) {
603
+ persistentBaseSnapshotCreating.delete(config.agentName);
604
+ }
572
605
  if (!toolResponse) {
573
606
  return {
574
607
  toolResponse: "Subagent workflow returned no response",
@@ -604,22 +637,60 @@ function createSubagentHandler(subagents) {
604
637
  };
605
638
  };
606
639
  const destroySubagentSandboxes = async () => {
607
- const handles = [...pendingDestroys.values()];
640
+ const entries = [...pendingDestroys.values()];
608
641
  pendingDestroys.clear();
609
642
  await Promise.all(
610
- handles.map(async (handle) => {
643
+ entries.map(async ({ agentName, sandboxId }) => {
644
+ const ops = agentSandboxOps.get(agentName);
645
+ if (!ops) {
646
+ workflow.log.warn(
647
+ "Skipping sandbox destroy \u2014 no sandbox.proxy registered for agent",
648
+ { agentName, sandboxId }
649
+ );
650
+ return;
651
+ }
611
652
  try {
612
- await handle.signal(destroySandboxSignal);
613
- await handle.result();
653
+ await ops.destroySandbox(sandboxId);
614
654
  } catch (err) {
615
- workflow.log.warn("Failed to signal destroySandbox to child workflow", {
655
+ workflow.log.warn("Failed to destroy subagent sandbox", {
656
+ agentName,
657
+ sandboxId,
616
658
  error: err
617
659
  });
618
660
  }
619
661
  })
620
662
  );
621
663
  };
622
- return { handler, destroySubagentSandboxes };
664
+ const cleanupSubagentSnapshots = async () => {
665
+ const tagged = [];
666
+ for (const entry of threadSnapshots.values()) tagged.push(entry);
667
+ for (const [agentName, snapshot] of persistentBaseSnapshot.entries()) {
668
+ tagged.push({ agentName, snapshot });
669
+ }
670
+ threadSnapshots.clear();
671
+ persistentBaseSnapshot.clear();
672
+ await Promise.all(
673
+ tagged.map(async ({ agentName, snapshot }) => {
674
+ const ops = agentSandboxOps.get(agentName);
675
+ if (!ops) {
676
+ workflow.log.warn(
677
+ "Skipping snapshot delete \u2014 no sandbox.proxy registered for agent",
678
+ { agentName }
679
+ );
680
+ return;
681
+ }
682
+ try {
683
+ await ops.deleteSandboxSnapshot(snapshot);
684
+ } catch (err) {
685
+ workflow.log.warn("Failed to delete subagent snapshot", {
686
+ agentName,
687
+ error: err
688
+ });
689
+ }
690
+ })
691
+ );
692
+ };
693
+ return { handler, destroySubagentSandboxes, cleanupSubagentSnapshots };
623
694
  }
624
695
 
625
696
  // src/lib/subagent/register.ts
@@ -633,7 +704,7 @@ function buildSubagentRegistration(subagents) {
633
704
  if (s.hooks) subagentHooksMap.set(s.agentName, s.hooks);
634
705
  }
635
706
  const resolveSubagentName = (args) => args.subagent;
636
- const { handler, destroySubagentSandboxes } = createSubagentHandler(subagents);
707
+ const { handler, destroySubagentSandboxes, cleanupSubagentSnapshots } = createSubagentHandler(subagents);
637
708
  const registration = {
638
709
  name: SUBAGENT_TOOL_NAME,
639
710
  enabled: () => getEnabled().length > 0,
@@ -657,7 +728,7 @@ function buildSubagentRegistration(subagents) {
657
728
  }
658
729
  }
659
730
  };
660
- return { registration, destroySubagentSandboxes };
731
+ return { registration, destroySubagentSandboxes, cleanupSubagentSnapshots };
661
732
  }
662
733
  var READ_SKILL_TOOL_NAME = "ReadSkill";
663
734
  function buildReadSkillDescription(skills) {
@@ -730,9 +801,7 @@ function validateSkillNames(skills) {
730
801
  const names = skills.map((s) => s.name);
731
802
  const dupes = names.filter((n, i) => names.indexOf(n) !== i);
732
803
  if (dupes.length > 0) {
733
- throw new Error(
734
- `Duplicate skill names: ${[...new Set(dupes)].join(", ")}`
735
- );
804
+ throw new Error(`Duplicate skill names: ${[...new Set(dupes)].join(", ")}`);
736
805
  }
737
806
  }
738
807
  function buildSkillRegistration(skills) {
@@ -802,11 +871,13 @@ async function createSession({
802
871
  } = threadOps;
803
872
  const plugins = [];
804
873
  let destroySubagentSandboxes;
874
+ let cleanupSubagentSnapshots;
805
875
  if (subagents) {
806
876
  const result = buildSubagentRegistration(subagents);
807
877
  if (result) {
808
878
  plugins.push(result.registration);
809
879
  destroySubagentSandboxes = result.destroySubagentSandboxes;
880
+ cleanupSubagentSnapshots = result.cleanupSubagentSnapshots;
810
881
  }
811
882
  }
812
883
  if (skills) {
@@ -859,6 +930,9 @@ async function createSession({
859
930
  const sandboxMode = sandboxInit?.mode;
860
931
  let sandboxId;
861
932
  let sandboxOwned = false;
933
+ let baseSnapshot;
934
+ let exitSnapshot;
935
+ let freshlyCreated = false;
862
936
  if (sandboxMode === "inherit") {
863
937
  const inheritInit = sandboxInit;
864
938
  sandboxId = inheritInit.sandboxId;
@@ -891,6 +965,16 @@ async function createSession({
891
965
  sandboxInit.sandboxId
892
966
  );
893
967
  sandboxOwned = true;
968
+ } else if (sandboxMode === "from-snapshot") {
969
+ if (!sandboxOps) {
970
+ throw workflow.ApplicationFailure.create({
971
+ message: "No sandboxOps provided \u2014 cannot restore sandbox",
972
+ nonRetryable: true
973
+ });
974
+ }
975
+ const snap = sandboxInit.snapshot;
976
+ sandboxId = await sandboxOps.restoreSandbox(snap);
977
+ sandboxOwned = true;
894
978
  } else if (sandboxOps) {
895
979
  const skillFiles = skills ? collectSkillFiles(skills) : void 0;
896
980
  const ctx = sandboxInit?.ctx;
@@ -899,9 +983,13 @@ async function createSession({
899
983
  if (result) {
900
984
  sandboxId = result.sandboxId;
901
985
  sandboxOwned = true;
986
+ freshlyCreated = true;
902
987
  }
903
988
  }
904
- if (sandboxId && onSandboxReady) {
989
+ if (sandboxId && sandboxOwned && freshlyCreated && sandboxShutdown === "snapshot" && sandboxOps) {
990
+ baseSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
991
+ }
992
+ if (sandboxId && sandboxOwned && onSandboxReady) {
905
993
  onSandboxReady(sandboxId);
906
994
  }
907
995
  if (virtualFsConfig) {
@@ -967,6 +1055,7 @@ async function createSession({
967
1055
  threadKey
968
1056
  );
969
1057
  let exitReason = "completed";
1058
+ let finalMessage = null;
970
1059
  try {
971
1060
  while (stateManager.isRunning() && !stateManager.isTerminal() && stateManager.getTurns() < maxTurns) {
972
1061
  stateManager.incrementTurns();
@@ -993,21 +1082,8 @@ async function createSession({
993
1082
  if (!toolRouter.hasTools() || rawToolCalls.length === 0) {
994
1083
  stateManager.complete();
995
1084
  exitReason = "completed";
996
- workflow.log.info("session ended", {
997
- agentName,
998
- threadId,
999
- exitReason,
1000
- turns: currentTurn,
1001
- durationMs: Date.now() - sessionStartMs,
1002
- usage: stateManager.getTotalUsage()
1003
- });
1004
- return {
1005
- threadId,
1006
- finalMessage: message,
1007
- exitReason,
1008
- usage: stateManager.getTotalUsage(),
1009
- sandboxId
1010
- };
1085
+ finalMessage = message;
1086
+ break;
1011
1087
  }
1012
1088
  const parsedToolCalls = [];
1013
1089
  for (const tc of rawToolCalls) {
@@ -1079,11 +1155,21 @@ async function createSession({
1079
1155
  case "pause-until-parent-close":
1080
1156
  await sandboxOps.pauseSandbox(sandboxId);
1081
1157
  break;
1158
+ case "keep":
1159
+ case "keep-until-parent-close":
1160
+ break;
1161
+ case "snapshot":
1162
+ exitSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
1163
+ await sandboxOps.destroySandbox(sandboxId);
1164
+ break;
1082
1165
  }
1083
1166
  }
1084
1167
  if (destroySubagentSandboxes) {
1085
1168
  await destroySubagentSandboxes();
1086
1169
  }
1170
+ if (cleanupSubagentSnapshots) {
1171
+ await cleanupSubagentSnapshots();
1172
+ }
1087
1173
  }
1088
1174
  workflow.log.info("session ended", {
1089
1175
  agentName,
@@ -1091,14 +1177,18 @@ async function createSession({
1091
1177
  exitReason,
1092
1178
  turns: stateManager.getTurns(),
1093
1179
  durationMs: Date.now() - sessionStartMs,
1094
- usage: stateManager.getTotalUsage()
1180
+ usage: stateManager.getTotalUsage(),
1181
+ ...baseSnapshot && { hasBaseSnapshot: true },
1182
+ ...exitSnapshot && { hasExitSnapshot: true }
1095
1183
  });
1096
1184
  return {
1097
1185
  threadId,
1098
- finalMessage: null,
1186
+ finalMessage,
1099
1187
  exitReason,
1100
1188
  usage: stateManager.getTotalUsage(),
1101
- sandboxId
1189
+ sandboxId,
1190
+ ...baseSnapshot && { baseSnapshot },
1191
+ ...exitSnapshot && { snapshot: exitSnapshot }
1102
1192
  };
1103
1193
  }
1104
1194
  };
@@ -1309,44 +1399,16 @@ function defineSubagentWorkflow(config, fn) {
1309
1399
  ...workflowInput.thread && { thread: workflowInput.thread },
1310
1400
  ...workflowInput.sandbox && { sandbox: workflowInput.sandbox },
1311
1401
  onSandboxReady: (sandboxId) => {
1312
- void parentHandle.signal(childSandboxReadySignal, {
1313
- childWorkflowId: workflow.workflowInfo().workflowId,
1314
- sandboxId
1315
- });
1402
+ const isReuse = workflowInput.sandbox?.mode === "continue";
1403
+ if (!isReuse) {
1404
+ void parentHandle.signal(childSandboxReadySignal, {
1405
+ childWorkflowId: workflow.workflowInfo().workflowId,
1406
+ sandboxId
1407
+ });
1408
+ }
1316
1409
  }
1317
1410
  };
1318
- const { destroySandbox, ...result } = await fn(
1319
- prompt,
1320
- sessionInput,
1321
- context ?? {}
1322
- );
1323
- if (effectiveShutdown === "pause-until-parent-close" || effectiveShutdown === "keep-until-parent-close") {
1324
- if (!destroySandbox) {
1325
- throw workflow.ApplicationFailure.create({
1326
- message: `Subagent "${config.name}" has sandboxShutdown="${effectiveShutdown}" but fn did not return a destroySandbox callback`,
1327
- nonRetryable: true
1328
- });
1329
- }
1330
- if (!result.sandboxId) {
1331
- throw workflow.ApplicationFailure.create({
1332
- message: `Subagent "${config.name}" has sandboxShutdown="${effectiveShutdown}" but fn did not return a sandboxId`,
1333
- nonRetryable: true
1334
- });
1335
- }
1336
- }
1337
- await parentHandle.signal(childResultSignal, {
1338
- childWorkflowId: workflow.workflowInfo().workflowId,
1339
- result
1340
- });
1341
- if (destroySandbox) {
1342
- let destroyRequested = false;
1343
- workflow.setHandler(destroySandboxSignal, () => {
1344
- destroyRequested = true;
1345
- });
1346
- await workflow.condition(() => destroyRequested);
1347
- await destroySandbox();
1348
- }
1349
- return result;
1411
+ return fn(prompt, sessionInput, context ?? {});
1350
1412
  };
1351
1413
  Object.defineProperty(workflow$1, "name", { value: config.name });
1352
1414
  return Object.assign(workflow$1, {
@@ -1456,9 +1518,7 @@ function applyVirtualTreeMutations(stateManager, mutations) {
1456
1518
  tree = tree.filter((e) => e.path !== m.path);
1457
1519
  break;
1458
1520
  case "update":
1459
- tree = tree.map(
1460
- (e) => e.path === m.path ? { ...e, ...m.entry } : e
1461
- );
1521
+ tree = tree.map((e) => e.path === m.path ? { ...e, ...m.entry } : e);
1462
1522
  break;
1463
1523
  }
1464
1524
  }
@@ -1517,7 +1577,9 @@ function formatVirtualFileTree(entries, opts = {}) {
1517
1577
  // src/lib/virtual-fs/queries.ts
1518
1578
  function hasFileWithMimeType(stateManager, pattern) {
1519
1579
  const tree = stateManager.get("fileTree");
1520
- const matchers = (Array.isArray(pattern) ? pattern : [pattern]).map(buildMatcher);
1580
+ const matchers = (Array.isArray(pattern) ? pattern : [pattern]).map(
1581
+ buildMatcher
1582
+ );
1521
1583
  return tree.some((entry) => {
1522
1584
  const meta = entry.metadata;
1523
1585
  const mime = meta?.mimeType;
@@ -1578,7 +1640,9 @@ function proxyVirtualFsOps(scope, options) {
1578
1640
  // src/lib/skills/parse.ts
1579
1641
  function parseSkillFile(raw) {
1580
1642
  const trimmed = raw.replace(/^\uFEFF/, "");
1581
- const match = trimmed.match(/^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*\r?\n?([\s\S]*)$/);
1643
+ const match = trimmed.match(
1644
+ /^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*\r?\n?([\s\S]*)$/
1645
+ );
1582
1646
  if (!match) {
1583
1647
  throw new Error(
1584
1648
  "SKILL.md must start with YAML frontmatter delimited by ---"
@@ -1850,7 +1914,9 @@ function createTaskGetHandler(stateManager) {
1850
1914
  const task = stateManager.getTask(args.taskId) ?? null;
1851
1915
  if (!task) {
1852
1916
  return {
1853
- toolResponse: JSON.stringify({ error: `Task not found: ${args.taskId}` }),
1917
+ toolResponse: JSON.stringify({
1918
+ error: `Task not found: ${args.taskId}`
1919
+ }),
1854
1920
  data: null
1855
1921
  };
1856
1922
  }
@@ -1893,7 +1959,9 @@ function createTaskUpdateHandler(stateManager) {
1893
1959
  const task = stateManager.getTask(args.taskId);
1894
1960
  if (!task) {
1895
1961
  return {
1896
- toolResponse: JSON.stringify({ error: `Task not found: ${args.taskId}` }),
1962
+ toolResponse: JSON.stringify({
1963
+ error: `Task not found: ${args.taskId}`
1964
+ }),
1897
1965
  data: null
1898
1966
  };
1899
1967
  }