zeitlich 0.2.35 → 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 (199) 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 +35 -6
  17. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  18. package/dist/adapters/sandbox/daytona/index.d.cts +3 -1
  19. package/dist/adapters/sandbox/daytona/index.d.ts +3 -1
  20. package/dist/adapters/sandbox/daytona/index.js +35 -6
  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/filesystem.ts +29 -6
  121. package/src/adapters/sandbox/daytona/index.ts +6 -0
  122. package/src/adapters/sandbox/daytona/proxy.ts +2 -0
  123. package/src/adapters/sandbox/e2b/filesystem.ts +5 -4
  124. package/src/adapters/sandbox/e2b/index.ts +63 -12
  125. package/src/adapters/sandbox/e2b/proxy.ts +2 -0
  126. package/src/adapters/sandbox/inmemory/index.ts +5 -0
  127. package/src/adapters/sandbox/inmemory/proxy.ts +2 -0
  128. package/src/adapters/thread/anthropic/activities.ts +49 -26
  129. package/src/adapters/thread/anthropic/model-invoker.ts +15 -6
  130. package/src/adapters/thread/anthropic/proxy.ts +6 -2
  131. package/src/adapters/thread/anthropic/thread-manager.test.ts +26 -7
  132. package/src/adapters/thread/anthropic/thread-manager.ts +60 -46
  133. package/src/adapters/thread/google-genai/activities.ts +7 -2
  134. package/src/adapters/thread/google-genai/model-invoker.ts +26 -8
  135. package/src/adapters/thread/google-genai/proxy.ts +6 -2
  136. package/src/adapters/thread/google-genai/thread-manager.test.ts +13 -3
  137. package/src/adapters/thread/google-genai/thread-manager.ts +54 -33
  138. package/src/adapters/thread/langchain/activities.ts +46 -24
  139. package/src/adapters/thread/langchain/hooks.test.ts +36 -49
  140. package/src/adapters/thread/langchain/hooks.ts +18 -5
  141. package/src/adapters/thread/langchain/model-invoker.ts +3 -3
  142. package/src/adapters/thread/langchain/proxy.ts +6 -2
  143. package/src/adapters/thread/langchain/thread-manager.test.ts +5 -1
  144. package/src/adapters/thread/langchain/thread-manager.ts +20 -9
  145. package/src/index.ts +4 -1
  146. package/src/lib/activity.ts +16 -6
  147. package/src/lib/hooks/types.ts +6 -6
  148. package/src/lib/lifecycle.ts +9 -1
  149. package/src/lib/model/proxy.ts +2 -2
  150. package/src/lib/observability/hooks.ts +4 -5
  151. package/src/lib/observability/index.ts +1 -4
  152. package/src/lib/sandbox/manager.ts +21 -4
  153. package/src/lib/sandbox/node-fs.ts +3 -6
  154. package/src/lib/sandbox/sandbox.test.ts +36 -3
  155. package/src/lib/sandbox/tree.integration.test.ts +10 -3
  156. package/src/lib/sandbox/types.ts +35 -1
  157. package/src/lib/session/session-edge-cases.integration.test.ts +51 -13
  158. package/src/lib/session/session.integration.test.ts +139 -0
  159. package/src/lib/session/session.ts +50 -19
  160. package/src/lib/session/types.ts +13 -5
  161. package/src/lib/skills/fs-provider.ts +12 -8
  162. package/src/lib/skills/handler.ts +1 -1
  163. package/src/lib/skills/parse.ts +3 -1
  164. package/src/lib/skills/register.ts +1 -3
  165. package/src/lib/skills/skills.integration.test.ts +25 -15
  166. package/src/lib/state/manager.integration.test.ts +12 -2
  167. package/src/lib/subagent/define.ts +1 -1
  168. package/src/lib/subagent/handler.ts +186 -71
  169. package/src/lib/subagent/index.ts +1 -5
  170. package/src/lib/subagent/register.ts +3 -2
  171. package/src/lib/subagent/signals.ts +1 -10
  172. package/src/lib/subagent/subagent.integration.test.ts +438 -156
  173. package/src/lib/subagent/tool.ts +4 -3
  174. package/src/lib/subagent/types.ts +50 -20
  175. package/src/lib/subagent/workflow.ts +9 -49
  176. package/src/lib/thread/id.test.ts +1 -1
  177. package/src/lib/thread/id.ts +1 -2
  178. package/src/lib/thread/proxy.ts +3 -4
  179. package/src/lib/thread/types.ts +11 -3
  180. package/src/lib/tool-router/index.ts +1 -5
  181. package/src/lib/tool-router/router-edge-cases.integration.test.ts +1 -1
  182. package/src/lib/tool-router/router.ts +3 -2
  183. package/src/lib/tool-router/types.ts +11 -3
  184. package/src/lib/tool-router/with-sandbox.ts +19 -5
  185. package/src/lib/virtual-fs/filesystem.ts +1 -1
  186. package/src/lib/virtual-fs/index.ts +5 -1
  187. package/src/lib/virtual-fs/mutations.ts +2 -4
  188. package/src/lib/virtual-fs/queries.ts +9 -5
  189. package/src/lib/virtual-fs/types.ts +4 -1
  190. package/src/lib/virtual-fs/virtual-fs.test.ts +9 -11
  191. package/src/lib/workflow.test.ts +7 -4
  192. package/src/lib/workflow.ts +1 -5
  193. package/src/tools/ask-user-question/tool.ts +1 -3
  194. package/src/tools/glob/handler.ts +1 -4
  195. package/src/tools/task-get/handler.ts +4 -5
  196. package/src/tools/task-list/handler.ts +1 -4
  197. package/src/tools/task-update/handler.ts +4 -5
  198. package/src/workflow.ts +20 -7
  199. package/tsup.config.ts +9 -6
package/dist/index.cjs CHANGED
@@ -395,9 +395,7 @@ function createSubagentTool(subagents) {
395
395
  schema
396
396
  };
397
397
  }
398
- var childResultSignal = workflow.defineSignal("childResult");
399
398
  var childSandboxReadySignal = workflow.defineSignal("childSandboxReady");
400
- var destroySandboxSignal = workflow.defineSignal("destroySandbox");
401
399
 
402
400
  // src/lib/subagent/handler.ts
403
401
  function resolveSandboxConfig(config) {
@@ -421,25 +419,27 @@ function resolveSandboxConfig(config) {
421
419
  }
422
420
  function createSubagentHandler(subagents) {
423
421
  const { taskQueue: parentTaskQueue } = workflow.workflowInfo();
424
- const childResults = /* @__PURE__ */ new Map();
422
+ const agentSandboxOps = /* @__PURE__ */ new Map();
423
+ for (const cfg of subagents) {
424
+ if (cfg.sandbox && cfg.sandbox !== "none") {
425
+ agentSandboxOps.set(cfg.agentName, cfg.sandbox.proxy(cfg.agentName));
426
+ }
427
+ }
425
428
  const pendingDestroys = /* @__PURE__ */ new Map();
426
429
  const threadSandboxes = /* @__PURE__ */ new Map();
427
430
  const persistentSandboxes = /* @__PURE__ */ new Map();
428
431
  const persistentSandboxCreating = /* @__PURE__ */ new Set();
429
432
  const lazyCreatorAgent = /* @__PURE__ */ new Map();
430
- workflow.setHandler(childResultSignal, ({ childWorkflowId, result }) => {
431
- childResults.set(childWorkflowId, result);
432
- });
433
- workflow.setHandler(
434
- childSandboxReadySignal,
435
- ({ childWorkflowId, sandboxId }) => {
436
- const agentName = lazyCreatorAgent.get(childWorkflowId);
437
- if (agentName && !persistentSandboxes.has(agentName)) {
438
- persistentSandboxes.set(agentName, sandboxId);
439
- lazyCreatorAgent.delete(childWorkflowId);
440
- }
433
+ const threadSnapshots = /* @__PURE__ */ new Map();
434
+ const persistentBaseSnapshot = /* @__PURE__ */ new Map();
435
+ const persistentBaseSnapshotCreating = /* @__PURE__ */ new Set();
436
+ workflow.setHandler(childSandboxReadySignal, ({ childWorkflowId, sandboxId }) => {
437
+ const agentName = lazyCreatorAgent.get(childWorkflowId);
438
+ if (agentName && !persistentSandboxes.has(agentName)) {
439
+ persistentSandboxes.set(agentName, sandboxId);
440
+ lazyCreatorAgent.delete(childWorkflowId);
441
441
  }
442
- );
442
+ });
443
443
  const handler = async (args, context) => {
444
444
  const config = subagents.find((s) => s.agentName === args.subagent);
445
445
  if (!config) {
@@ -450,6 +450,12 @@ function createSubagentHandler(subagents) {
450
450
  const childWorkflowId = `${args.subagent}-${getShortId()}`;
451
451
  const { sandboxId: parentSandboxId } = context;
452
452
  const sandboxCfg = resolveSandboxConfig(config.sandbox);
453
+ if (sandboxCfg.source !== "none" && !agentSandboxOps.has(config.agentName)) {
454
+ throw workflow.ApplicationFailure.create({
455
+ message: `Subagent "${config.agentName}" uses a sandbox but no \`sandbox.proxy\` is configured on its SubagentConfig`,
456
+ nonRetryable: true
457
+ });
458
+ }
453
459
  if (sandboxCfg.source === "inherit" && !parentSandboxId) {
454
460
  throw new Error(
455
461
  `Subagent "${config.agentName}" is configured with sandbox: "inherit" but the parent has no sandbox`
@@ -468,12 +474,39 @@ function createSubagentHandler(subagents) {
468
474
  let sandbox;
469
475
  let sandboxShutdownOverride;
470
476
  let isLazyCreator = false;
477
+ let isSnapshotBaseCreator = false;
471
478
  if (sandboxCfg.source === "inherit" && parentSandboxId) {
472
479
  if (sandboxCfg.continuation === "fork") {
473
480
  sandbox = { mode: "fork", sandboxId: parentSandboxId };
481
+ } else if (sandboxCfg.continuation === "snapshot") {
482
+ throw new Error(
483
+ `Subagent "${config.agentName}" has sandbox source "inherit" with continuation "snapshot" \u2014 snapshot continuation is only supported for source "own"`
484
+ );
474
485
  } else {
475
486
  sandbox = { mode: "inherit", sandboxId: parentSandboxId };
476
487
  }
488
+ } else if (sandboxCfg.source === "own" && sandboxCfg.continuation === "snapshot") {
489
+ const isLazy = sandboxCfg.init === "once";
490
+ let baseSnap;
491
+ if (continuationThreadId) {
492
+ baseSnap = threadSnapshots.get(continuationThreadId)?.snapshot;
493
+ }
494
+ if (!baseSnap && isLazy) {
495
+ baseSnap = persistentBaseSnapshot.get(config.agentName);
496
+ if (!baseSnap) {
497
+ if (persistentBaseSnapshotCreating.has(config.agentName)) {
498
+ await workflow.condition(() => persistentBaseSnapshot.has(config.agentName));
499
+ baseSnap = persistentBaseSnapshot.get(config.agentName);
500
+ } else {
501
+ persistentBaseSnapshotCreating.add(config.agentName);
502
+ isSnapshotBaseCreator = true;
503
+ }
504
+ }
505
+ }
506
+ if (baseSnap) {
507
+ sandbox = { mode: "from-snapshot", snapshot: baseSnap };
508
+ }
509
+ sandboxShutdownOverride = "snapshot";
477
510
  } else if (sandboxCfg.source === "own") {
478
511
  const isLazy = sandboxCfg.init === "once";
479
512
  let baseSandboxId;
@@ -524,31 +557,8 @@ function createSubagentHandler(subagents) {
524
557
  threadMode,
525
558
  sandboxSource: sandboxCfg.source
526
559
  });
527
- const childHandle = await workflow.startChild(config.workflow, childOpts);
560
+ const childResult = await workflow.executeChild(config.workflow, childOpts);
528
561
  const effectiveShutdown = sandboxShutdownOverride ?? sandboxCfg.shutdown ?? "destroy";
529
- if (effectiveShutdown === "pause-until-parent-close" || effectiveShutdown === "keep-until-parent-close") {
530
- const key = isLazyCreator ? `persistent:${config.agentName}` : childWorkflowId;
531
- pendingDestroys.set(key, childHandle);
532
- }
533
- await Promise.race([
534
- workflow.condition(() => childResults.has(childWorkflowId)),
535
- childHandle.result()
536
- ]);
537
- if (!childResults.has(childWorkflowId)) {
538
- await workflow.condition(() => childResults.has(childWorkflowId));
539
- }
540
- const childResult = childResults.get(childWorkflowId);
541
- childResults.delete(childWorkflowId);
542
- if (!childResult) {
543
- workflow.log.warn("subagent returned no result", {
544
- subagent: config.agentName,
545
- childWorkflowId
546
- });
547
- return {
548
- toolResponse: "Subagent workflow did not signal a result",
549
- data: null
550
- };
551
- }
552
562
  workflow.log.info("subagent completed", {
553
563
  subagent: config.agentName,
554
564
  childWorkflowId,
@@ -560,19 +570,42 @@ function createSubagentHandler(subagents) {
560
570
  usage,
561
571
  threadId: childThreadId,
562
572
  sandboxId: childSandboxId,
573
+ snapshot: childSnapshot,
574
+ baseSnapshot: childBaseSnapshot,
563
575
  metadata
564
576
  } = childResult;
565
577
  if (childSandboxId) {
566
- if (sandboxCfg.source === "own" && sandboxCfg.init === "once" && !persistentSandboxes.has(config.agentName)) {
578
+ if (sandboxCfg.source === "own" && sandboxCfg.init === "once" && sandboxCfg.continuation !== "snapshot" && !persistentSandboxes.has(config.agentName)) {
567
579
  persistentSandboxes.set(config.agentName, childSandboxId);
568
- } else if (allowsContinuation && childThreadId) {
580
+ } else if (allowsContinuation && childThreadId && sandboxCfg.source === "own" && sandboxCfg.continuation !== "snapshot") {
569
581
  threadSandboxes.set(childThreadId, childSandboxId);
570
582
  }
571
583
  }
584
+ if (childSandboxId && (effectiveShutdown === "pause-until-parent-close" || effectiveShutdown === "keep-until-parent-close")) {
585
+ const key = isLazyCreator ? `persistent:${config.agentName}` : childWorkflowId;
586
+ pendingDestroys.set(key, {
587
+ agentName: config.agentName,
588
+ sandboxId: childSandboxId
589
+ });
590
+ }
591
+ if (sandboxCfg.source === "own" && sandboxCfg.continuation === "snapshot") {
592
+ if (childSnapshot && childThreadId) {
593
+ threadSnapshots.set(childThreadId, {
594
+ agentName: config.agentName,
595
+ snapshot: childSnapshot
596
+ });
597
+ }
598
+ if (isSnapshotBaseCreator && childBaseSnapshot && !persistentBaseSnapshot.has(config.agentName)) {
599
+ persistentBaseSnapshot.set(config.agentName, childBaseSnapshot);
600
+ }
601
+ }
572
602
  if (isLazyCreator) {
573
603
  persistentSandboxCreating.delete(config.agentName);
574
604
  lazyCreatorAgent.delete(childWorkflowId);
575
605
  }
606
+ if (isSnapshotBaseCreator) {
607
+ persistentBaseSnapshotCreating.delete(config.agentName);
608
+ }
576
609
  if (!toolResponse) {
577
610
  return {
578
611
  toolResponse: "Subagent workflow returned no response",
@@ -608,22 +641,60 @@ function createSubagentHandler(subagents) {
608
641
  };
609
642
  };
610
643
  const destroySubagentSandboxes = async () => {
611
- const handles = [...pendingDestroys.values()];
644
+ const entries = [...pendingDestroys.values()];
612
645
  pendingDestroys.clear();
613
646
  await Promise.all(
614
- handles.map(async (handle) => {
647
+ entries.map(async ({ agentName, sandboxId }) => {
648
+ const ops = agentSandboxOps.get(agentName);
649
+ if (!ops) {
650
+ workflow.log.warn(
651
+ "Skipping sandbox destroy \u2014 no sandbox.proxy registered for agent",
652
+ { agentName, sandboxId }
653
+ );
654
+ return;
655
+ }
656
+ try {
657
+ await ops.destroySandbox(sandboxId);
658
+ } catch (err) {
659
+ workflow.log.warn("Failed to destroy subagent sandbox", {
660
+ agentName,
661
+ sandboxId,
662
+ error: err
663
+ });
664
+ }
665
+ })
666
+ );
667
+ };
668
+ const cleanupSubagentSnapshots = async () => {
669
+ const tagged = [];
670
+ for (const entry of threadSnapshots.values()) tagged.push(entry);
671
+ for (const [agentName, snapshot] of persistentBaseSnapshot.entries()) {
672
+ tagged.push({ agentName, snapshot });
673
+ }
674
+ threadSnapshots.clear();
675
+ persistentBaseSnapshot.clear();
676
+ await Promise.all(
677
+ tagged.map(async ({ agentName, snapshot }) => {
678
+ const ops = agentSandboxOps.get(agentName);
679
+ if (!ops) {
680
+ workflow.log.warn(
681
+ "Skipping snapshot delete \u2014 no sandbox.proxy registered for agent",
682
+ { agentName }
683
+ );
684
+ return;
685
+ }
615
686
  try {
616
- await handle.signal(destroySandboxSignal);
617
- await handle.result();
687
+ await ops.deleteSandboxSnapshot(snapshot);
618
688
  } catch (err) {
619
- workflow.log.warn("Failed to signal destroySandbox to child workflow", {
689
+ workflow.log.warn("Failed to delete subagent snapshot", {
690
+ agentName,
620
691
  error: err
621
692
  });
622
693
  }
623
694
  })
624
695
  );
625
696
  };
626
- return { handler, destroySubagentSandboxes };
697
+ return { handler, destroySubagentSandboxes, cleanupSubagentSnapshots };
627
698
  }
628
699
 
629
700
  // src/lib/subagent/register.ts
@@ -637,7 +708,7 @@ function buildSubagentRegistration(subagents) {
637
708
  if (s.hooks) subagentHooksMap.set(s.agentName, s.hooks);
638
709
  }
639
710
  const resolveSubagentName = (args) => args.subagent;
640
- const { handler, destroySubagentSandboxes } = createSubagentHandler(subagents);
711
+ const { handler, destroySubagentSandboxes, cleanupSubagentSnapshots } = createSubagentHandler(subagents);
641
712
  const registration = {
642
713
  name: SUBAGENT_TOOL_NAME,
643
714
  enabled: () => getEnabled().length > 0,
@@ -661,7 +732,7 @@ function buildSubagentRegistration(subagents) {
661
732
  }
662
733
  }
663
734
  };
664
- return { registration, destroySubagentSandboxes };
735
+ return { registration, destroySubagentSandboxes, cleanupSubagentSnapshots };
665
736
  }
666
737
  var READ_SKILL_TOOL_NAME = "ReadSkill";
667
738
  function buildReadSkillDescription(skills) {
@@ -734,9 +805,7 @@ function validateSkillNames(skills) {
734
805
  const names = skills.map((s) => s.name);
735
806
  const dupes = names.filter((n, i) => names.indexOf(n) !== i);
736
807
  if (dupes.length > 0) {
737
- throw new Error(
738
- `Duplicate skill names: ${[...new Set(dupes)].join(", ")}`
739
- );
808
+ throw new Error(`Duplicate skill names: ${[...new Set(dupes)].join(", ")}`);
740
809
  }
741
810
  }
742
811
  function buildSkillRegistration(skills) {
@@ -806,11 +875,13 @@ async function createSession({
806
875
  } = threadOps;
807
876
  const plugins = [];
808
877
  let destroySubagentSandboxes;
878
+ let cleanupSubagentSnapshots;
809
879
  if (subagents) {
810
880
  const result = buildSubagentRegistration(subagents);
811
881
  if (result) {
812
882
  plugins.push(result.registration);
813
883
  destroySubagentSandboxes = result.destroySubagentSandboxes;
884
+ cleanupSubagentSnapshots = result.cleanupSubagentSnapshots;
814
885
  }
815
886
  }
816
887
  if (skills) {
@@ -863,6 +934,9 @@ async function createSession({
863
934
  const sandboxMode = sandboxInit?.mode;
864
935
  let sandboxId;
865
936
  let sandboxOwned = false;
937
+ let baseSnapshot;
938
+ let exitSnapshot;
939
+ let freshlyCreated = false;
866
940
  if (sandboxMode === "inherit") {
867
941
  const inheritInit = sandboxInit;
868
942
  sandboxId = inheritInit.sandboxId;
@@ -895,6 +969,16 @@ async function createSession({
895
969
  sandboxInit.sandboxId
896
970
  );
897
971
  sandboxOwned = true;
972
+ } else if (sandboxMode === "from-snapshot") {
973
+ if (!sandboxOps) {
974
+ throw workflow.ApplicationFailure.create({
975
+ message: "No sandboxOps provided \u2014 cannot restore sandbox",
976
+ nonRetryable: true
977
+ });
978
+ }
979
+ const snap = sandboxInit.snapshot;
980
+ sandboxId = await sandboxOps.restoreSandbox(snap);
981
+ sandboxOwned = true;
898
982
  } else if (sandboxOps) {
899
983
  const skillFiles = skills ? collectSkillFiles(skills) : void 0;
900
984
  const ctx = sandboxInit?.ctx;
@@ -903,9 +987,13 @@ async function createSession({
903
987
  if (result) {
904
988
  sandboxId = result.sandboxId;
905
989
  sandboxOwned = true;
990
+ freshlyCreated = true;
906
991
  }
907
992
  }
908
- if (sandboxId && onSandboxReady) {
993
+ if (sandboxId && sandboxOwned && freshlyCreated && sandboxShutdown === "snapshot" && sandboxOps) {
994
+ baseSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
995
+ }
996
+ if (sandboxId && sandboxOwned && onSandboxReady) {
909
997
  onSandboxReady(sandboxId);
910
998
  }
911
999
  if (virtualFsConfig) {
@@ -971,6 +1059,7 @@ async function createSession({
971
1059
  threadKey
972
1060
  );
973
1061
  let exitReason = "completed";
1062
+ let finalMessage = null;
974
1063
  try {
975
1064
  while (stateManager.isRunning() && !stateManager.isTerminal() && stateManager.getTurns() < maxTurns) {
976
1065
  stateManager.incrementTurns();
@@ -997,21 +1086,8 @@ async function createSession({
997
1086
  if (!toolRouter.hasTools() || rawToolCalls.length === 0) {
998
1087
  stateManager.complete();
999
1088
  exitReason = "completed";
1000
- workflow.log.info("session ended", {
1001
- agentName,
1002
- threadId,
1003
- exitReason,
1004
- turns: currentTurn,
1005
- durationMs: Date.now() - sessionStartMs,
1006
- usage: stateManager.getTotalUsage()
1007
- });
1008
- return {
1009
- threadId,
1010
- finalMessage: message,
1011
- exitReason,
1012
- usage: stateManager.getTotalUsage(),
1013
- sandboxId
1014
- };
1089
+ finalMessage = message;
1090
+ break;
1015
1091
  }
1016
1092
  const parsedToolCalls = [];
1017
1093
  for (const tc of rawToolCalls) {
@@ -1083,11 +1159,21 @@ async function createSession({
1083
1159
  case "pause-until-parent-close":
1084
1160
  await sandboxOps.pauseSandbox(sandboxId);
1085
1161
  break;
1162
+ case "keep":
1163
+ case "keep-until-parent-close":
1164
+ break;
1165
+ case "snapshot":
1166
+ exitSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
1167
+ await sandboxOps.destroySandbox(sandboxId);
1168
+ break;
1086
1169
  }
1087
1170
  }
1088
1171
  if (destroySubagentSandboxes) {
1089
1172
  await destroySubagentSandboxes();
1090
1173
  }
1174
+ if (cleanupSubagentSnapshots) {
1175
+ await cleanupSubagentSnapshots();
1176
+ }
1091
1177
  }
1092
1178
  workflow.log.info("session ended", {
1093
1179
  agentName,
@@ -1095,14 +1181,18 @@ async function createSession({
1095
1181
  exitReason,
1096
1182
  turns: stateManager.getTurns(),
1097
1183
  durationMs: Date.now() - sessionStartMs,
1098
- usage: stateManager.getTotalUsage()
1184
+ usage: stateManager.getTotalUsage(),
1185
+ ...baseSnapshot && { hasBaseSnapshot: true },
1186
+ ...exitSnapshot && { hasExitSnapshot: true }
1099
1187
  });
1100
1188
  return {
1101
1189
  threadId,
1102
- finalMessage: null,
1190
+ finalMessage,
1103
1191
  exitReason,
1104
1192
  usage: stateManager.getTotalUsage(),
1105
- sandboxId
1193
+ sandboxId,
1194
+ ...baseSnapshot && { baseSnapshot },
1195
+ ...exitSnapshot && { snapshot: exitSnapshot }
1106
1196
  };
1107
1197
  }
1108
1198
  };
@@ -1391,44 +1481,16 @@ function defineSubagentWorkflow(config, fn) {
1391
1481
  ...workflowInput.thread && { thread: workflowInput.thread },
1392
1482
  ...workflowInput.sandbox && { sandbox: workflowInput.sandbox },
1393
1483
  onSandboxReady: (sandboxId) => {
1394
- void parentHandle.signal(childSandboxReadySignal, {
1395
- childWorkflowId: workflow.workflowInfo().workflowId,
1396
- sandboxId
1397
- });
1484
+ const isReuse = workflowInput.sandbox?.mode === "continue";
1485
+ if (!isReuse) {
1486
+ void parentHandle.signal(childSandboxReadySignal, {
1487
+ childWorkflowId: workflow.workflowInfo().workflowId,
1488
+ sandboxId
1489
+ });
1490
+ }
1398
1491
  }
1399
1492
  };
1400
- const { destroySandbox, ...result } = await fn(
1401
- prompt,
1402
- sessionInput,
1403
- context ?? {}
1404
- );
1405
- if (effectiveShutdown === "pause-until-parent-close" || effectiveShutdown === "keep-until-parent-close") {
1406
- if (!destroySandbox) {
1407
- throw workflow.ApplicationFailure.create({
1408
- message: `Subagent "${config.name}" has sandboxShutdown="${effectiveShutdown}" but fn did not return a destroySandbox callback`,
1409
- nonRetryable: true
1410
- });
1411
- }
1412
- if (!result.sandboxId) {
1413
- throw workflow.ApplicationFailure.create({
1414
- message: `Subagent "${config.name}" has sandboxShutdown="${effectiveShutdown}" but fn did not return a sandboxId`,
1415
- nonRetryable: true
1416
- });
1417
- }
1418
- }
1419
- await parentHandle.signal(childResultSignal, {
1420
- childWorkflowId: workflow.workflowInfo().workflowId,
1421
- result
1422
- });
1423
- if (destroySandbox) {
1424
- let destroyRequested = false;
1425
- workflow.setHandler(destroySandboxSignal, () => {
1426
- destroyRequested = true;
1427
- });
1428
- await workflow.condition(() => destroyRequested);
1429
- await destroySandbox();
1430
- }
1431
- return result;
1493
+ return fn(prompt, sessionInput, context ?? {});
1432
1494
  };
1433
1495
  Object.defineProperty(workflow$1, "name", { value: config.name });
1434
1496
  return Object.assign(workflow$1, {
@@ -1538,9 +1600,7 @@ function applyVirtualTreeMutations(stateManager, mutations) {
1538
1600
  tree = tree.filter((e) => e.path !== m.path);
1539
1601
  break;
1540
1602
  case "update":
1541
- tree = tree.map(
1542
- (e) => e.path === m.path ? { ...e, ...m.entry } : e
1543
- );
1603
+ tree = tree.map((e) => e.path === m.path ? { ...e, ...m.entry } : e);
1544
1604
  break;
1545
1605
  }
1546
1606
  }
@@ -1599,7 +1659,9 @@ function formatVirtualFileTree(entries, opts = {}) {
1599
1659
  // src/lib/virtual-fs/queries.ts
1600
1660
  function hasFileWithMimeType(stateManager, pattern) {
1601
1661
  const tree = stateManager.get("fileTree");
1602
- const matchers = (Array.isArray(pattern) ? pattern : [pattern]).map(buildMatcher);
1662
+ const matchers = (Array.isArray(pattern) ? pattern : [pattern]).map(
1663
+ buildMatcher
1664
+ );
1603
1665
  return tree.some((entry) => {
1604
1666
  const meta = entry.metadata;
1605
1667
  const mime = meta?.mimeType;
@@ -1660,7 +1722,9 @@ function proxyVirtualFsOps(scope, options) {
1660
1722
  // src/lib/skills/parse.ts
1661
1723
  function parseSkillFile(raw) {
1662
1724
  const trimmed = raw.replace(/^\uFEFF/, "");
1663
- const match = trimmed.match(/^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*\r?\n?([\s\S]*)$/);
1725
+ const match = trimmed.match(
1726
+ /^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*\r?\n?([\s\S]*)$/
1727
+ );
1664
1728
  if (!match) {
1665
1729
  throw new Error(
1666
1730
  "SKILL.md must start with YAML frontmatter delimited by ---"
@@ -1932,7 +1996,9 @@ function createTaskGetHandler(stateManager) {
1932
1996
  const task = stateManager.getTask(args.taskId) ?? null;
1933
1997
  if (!task) {
1934
1998
  return {
1935
- toolResponse: JSON.stringify({ error: `Task not found: ${args.taskId}` }),
1999
+ toolResponse: JSON.stringify({
2000
+ error: `Task not found: ${args.taskId}`
2001
+ }),
1936
2002
  data: null
1937
2003
  };
1938
2004
  }
@@ -1975,7 +2041,9 @@ function createTaskUpdateHandler(stateManager) {
1975
2041
  const task = stateManager.getTask(args.taskId);
1976
2042
  if (!task) {
1977
2043
  return {
1978
- toolResponse: JSON.stringify({ error: `Task not found: ${args.taskId}` }),
2044
+ toolResponse: JSON.stringify({
2045
+ error: `Task not found: ${args.taskId}`
2046
+ }),
1979
2047
  data: null
1980
2048
  };
1981
2049
  }
@@ -2085,6 +2153,8 @@ var FileSystemSkillProvider = class {
2085
2153
  this.fs = fs;
2086
2154
  this.baseDir = baseDir;
2087
2155
  }
2156
+ fs;
2157
+ baseDir;
2088
2158
  async listSkills() {
2089
2159
  const dirs = await this.discoverSkillDirs();
2090
2160
  const skills = [];
@@ -2100,9 +2170,7 @@ var FileSystemSkillProvider = class {
2100
2170
  return skills;
2101
2171
  }
2102
2172
  async getSkill(name) {
2103
- const raw = await this.fs.readFile(
2104
- path.join(this.baseDir, name, "SKILL.md")
2105
- );
2173
+ const raw = await this.fs.readFile(path.join(this.baseDir, name, "SKILL.md"));
2106
2174
  const { frontmatter, body } = parseSkillFile(raw);
2107
2175
  if (frontmatter.name !== name) {
2108
2176
  throw new Error(
@@ -2111,7 +2179,10 @@ var FileSystemSkillProvider = class {
2111
2179
  }
2112
2180
  const location = path.join(this.baseDir, name);
2113
2181
  const resourcePaths = await this.discoverResources(name);
2114
- const resourceContents = await this.readResourceContents(location, resourcePaths);
2182
+ const resourceContents = await this.readResourceContents(
2183
+ location,
2184
+ resourcePaths
2185
+ );
2115
2186
  return {
2116
2187
  ...frontmatter,
2117
2188
  instructions: body,
@@ -2131,7 +2202,10 @@ var FileSystemSkillProvider = class {
2131
2202
  const { frontmatter, body } = parseSkillFile(raw);
2132
2203
  const location = path.join(this.baseDir, dir);
2133
2204
  const resourcePaths = await this.discoverResources(dir);
2134
- const resourceContents = await this.readResourceContents(location, resourcePaths);
2205
+ const resourceContents = await this.readResourceContents(
2206
+ location,
2207
+ resourcePaths
2208
+ );
2135
2209
  skills.push({
2136
2210
  ...frontmatter,
2137
2211
  instructions: body,
@@ -2305,6 +2379,7 @@ var SandboxManager = class {
2305
2379
  this.provider = provider;
2306
2380
  this.hooks = options?.hooks ?? {};
2307
2381
  }
2382
+ provider;
2308
2383
  hooks;
2309
2384
  async create(options, ctx) {
2310
2385
  let providerOptions = options;
@@ -2333,7 +2408,7 @@ var SandboxManager = class {
2333
2408
  }
2334
2409
  const { sandbox } = await this.provider.create(providerOptions);
2335
2410
  if (this.hooks.onPostCreate) {
2336
- await this.hooks.onPostCreate(sandbox.id);
2411
+ await this.hooks.onPostCreate(sandbox, ctx ?? {});
2337
2412
  }
2338
2413
  return { sandboxId: sandbox.id };
2339
2414
  }
@@ -2356,6 +2431,9 @@ var SandboxManager = class {
2356
2431
  const sandbox = await this.provider.restore(snapshot);
2357
2432
  return sandbox.id;
2358
2433
  }
2434
+ async deleteSnapshot(snapshot) {
2435
+ await this.provider.deleteSnapshot(snapshot);
2436
+ }
2359
2437
  async fork(sandboxId) {
2360
2438
  const sandbox = await this.provider.fork(sandboxId);
2361
2439
  return sandbox.id;
@@ -2398,6 +2476,12 @@ var SandboxManager = class {
2398
2476
  snapshotSandbox: async (sandboxId) => {
2399
2477
  return this.snapshot(sandboxId);
2400
2478
  },
2479
+ restoreSandbox: async (snapshot) => {
2480
+ return this.restore(snapshot);
2481
+ },
2482
+ deleteSandboxSnapshot: async (snapshot) => {
2483
+ await this.deleteSnapshot(snapshot);
2484
+ },
2401
2485
  forkSandbox: async (sandboxId) => {
2402
2486
  return this.fork(sandboxId);
2403
2487
  }
@@ -2513,6 +2597,8 @@ var VirtualFileSystem = class {
2513
2597
  ]) : []
2514
2598
  );
2515
2599
  }
2600
+ resolver;
2601
+ ctx;
2516
2602
  workspaceBase;
2517
2603
  entries;
2518
2604
  directories;