zeitlich 0.2.40 → 0.2.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/README.md +12 -1
  2. package/dist/{activities-CvUrG3YG.d.cts → activities-Coafq5zr.d.cts} +2 -2
  3. package/dist/{activities-CULxRzJ1.d.ts → activities-CrN-ghLo.d.ts} +2 -2
  4. package/dist/adapters/sandbox/daytona/index.cjs +4 -23
  5. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  6. package/dist/adapters/sandbox/daytona/index.d.cts +18 -86
  7. package/dist/adapters/sandbox/daytona/index.d.ts +18 -86
  8. package/dist/adapters/sandbox/daytona/index.js +4 -23
  9. package/dist/adapters/sandbox/daytona/index.js.map +1 -1
  10. package/dist/adapters/sandbox/daytona/workflow.cjs +1 -7
  11. package/dist/adapters/sandbox/daytona/workflow.cjs.map +1 -1
  12. package/dist/adapters/sandbox/daytona/workflow.d.cts +9 -2
  13. package/dist/adapters/sandbox/daytona/workflow.d.ts +9 -2
  14. package/dist/adapters/sandbox/daytona/workflow.js +1 -7
  15. package/dist/adapters/sandbox/daytona/workflow.js.map +1 -1
  16. package/dist/adapters/sandbox/e2b/index.cjs +21 -3
  17. package/dist/adapters/sandbox/e2b/index.cjs.map +1 -1
  18. package/dist/adapters/sandbox/e2b/index.d.cts +48 -7
  19. package/dist/adapters/sandbox/e2b/index.d.ts +48 -7
  20. package/dist/adapters/sandbox/e2b/index.js +22 -5
  21. package/dist/adapters/sandbox/e2b/index.js.map +1 -1
  22. package/dist/adapters/sandbox/e2b/workflow.cjs.map +1 -1
  23. package/dist/adapters/sandbox/e2b/workflow.d.cts +4 -2
  24. package/dist/adapters/sandbox/e2b/workflow.d.ts +4 -2
  25. package/dist/adapters/sandbox/e2b/workflow.js.map +1 -1
  26. package/dist/adapters/sandbox/inmemory/index.cjs +11 -0
  27. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
  28. package/dist/adapters/sandbox/inmemory/index.d.cts +11 -3
  29. package/dist/adapters/sandbox/inmemory/index.d.ts +11 -3
  30. package/dist/adapters/sandbox/inmemory/index.js +11 -1
  31. package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
  32. package/dist/adapters/sandbox/inmemory/workflow.cjs.map +1 -1
  33. package/dist/adapters/sandbox/inmemory/workflow.d.cts +4 -2
  34. package/dist/adapters/sandbox/inmemory/workflow.d.ts +4 -2
  35. package/dist/adapters/sandbox/inmemory/workflow.js.map +1 -1
  36. package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
  37. package/dist/adapters/thread/anthropic/index.d.cts +6 -6
  38. package/dist/adapters/thread/anthropic/index.d.ts +6 -6
  39. package/dist/adapters/thread/anthropic/index.js.map +1 -1
  40. package/dist/adapters/thread/anthropic/workflow.d.cts +6 -6
  41. package/dist/adapters/thread/anthropic/workflow.d.ts +6 -6
  42. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  43. package/dist/adapters/thread/google-genai/index.d.cts +6 -6
  44. package/dist/adapters/thread/google-genai/index.d.ts +6 -6
  45. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  46. package/dist/adapters/thread/google-genai/workflow.d.cts +6 -6
  47. package/dist/adapters/thread/google-genai/workflow.d.ts +6 -6
  48. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  49. package/dist/adapters/thread/langchain/index.d.cts +6 -6
  50. package/dist/adapters/thread/langchain/index.d.ts +6 -6
  51. package/dist/adapters/thread/langchain/index.js.map +1 -1
  52. package/dist/adapters/thread/langchain/workflow.d.cts +6 -6
  53. package/dist/adapters/thread/langchain/workflow.d.ts +6 -6
  54. package/dist/index.cjs +316 -119
  55. package/dist/index.cjs.map +1 -1
  56. package/dist/index.d.cts +93 -17
  57. package/dist/index.d.ts +93 -17
  58. package/dist/index.js +317 -120
  59. package/dist/index.js.map +1 -1
  60. package/dist/{proxy-5EbwzaY4.d.cts → proxy-Bf7uI-Hw.d.cts} +1 -1
  61. package/dist/{proxy-wZufFfBh.d.ts → proxy-COqA95FW.d.ts} +1 -1
  62. package/dist/{thread-manager-BqBAIsED.d.ts → thread-manager-BhkOyQ1I.d.ts} +2 -2
  63. package/dist/{thread-manager-BNiIt5r8.d.ts → thread-manager-Bi1XlbpJ.d.ts} +2 -2
  64. package/dist/{thread-manager-DF8WuCRs.d.cts → thread-manager-BsLO3Fgc.d.cts} +2 -2
  65. package/dist/{thread-manager-BoN5DOvG.d.cts → thread-manager-wRVVBFgj.d.cts} +2 -2
  66. package/dist/{types-C7OoY7h8.d.ts → types-BkX4HLzi.d.ts} +1 -1
  67. package/dist/{types-CuISs0Ub.d.cts → types-C66-BVBr.d.cts} +1 -1
  68. package/dist/types-CJ7tCdl6.d.cts +266 -0
  69. package/dist/types-CJ7tCdl6.d.ts +266 -0
  70. package/dist/{types-DeQH84C_.d.ts → types-CdALEF3z.d.cts} +342 -23
  71. package/dist/{types-Cn2r3ol3.d.cts → types-ChAy_jSP.d.ts} +342 -23
  72. package/dist/types-CjY93AWZ.d.cts +84 -0
  73. package/dist/types-gVa5XCWD.d.ts +84 -0
  74. package/dist/{workflow-DhplIN65.d.cts → workflow-BwT5EybR.d.ts} +7 -6
  75. package/dist/{workflow-C2MZZj5K.d.ts → workflow-DMmiaw6w.d.cts} +7 -6
  76. package/dist/workflow.cjs +138 -77
  77. package/dist/workflow.cjs.map +1 -1
  78. package/dist/workflow.d.cts +4 -4
  79. package/dist/workflow.d.ts +4 -4
  80. package/dist/workflow.js +139 -78
  81. package/dist/workflow.js.map +1 -1
  82. package/package.json +17 -33
  83. package/src/adapters/sandbox/daytona/index.ts +25 -48
  84. package/src/adapters/sandbox/daytona/proxy.ts +7 -8
  85. package/src/adapters/sandbox/e2b/README.md +81 -0
  86. package/src/adapters/sandbox/e2b/index.ts +53 -11
  87. package/src/adapters/sandbox/e2b/keep-alive.test.ts +115 -0
  88. package/src/adapters/sandbox/e2b/proxy.ts +3 -2
  89. package/src/adapters/sandbox/e2b/types.ts +34 -2
  90. package/src/adapters/sandbox/inmemory/index.ts +21 -1
  91. package/src/adapters/sandbox/inmemory/proxy.ts +7 -3
  92. package/src/index.ts +1 -1
  93. package/src/lib/activity.ts +5 -0
  94. package/src/lib/sandbox/capability-types.test.ts +859 -0
  95. package/src/lib/sandbox/index.ts +1 -0
  96. package/src/lib/sandbox/manager.ts +187 -31
  97. package/src/lib/sandbox/types.ts +189 -46
  98. package/src/lib/session/index.ts +1 -0
  99. package/src/lib/session/session.integration.test.ts +58 -0
  100. package/src/lib/session/session.ts +109 -50
  101. package/src/lib/session/types.ts +189 -8
  102. package/src/lib/subagent/handler.ts +66 -43
  103. package/src/lib/subagent/subagent.integration.test.ts +2 -0
  104. package/src/lib/subagent/types.ts +492 -16
  105. package/src/lib/subagent/workflow.ts +11 -1
  106. package/src/lib/tool-router/auto-append-sandbox.integration.test.ts +158 -0
  107. package/src/lib/tool-router/index.ts +1 -1
  108. package/src/lib/tool-router/with-sandbox.ts +45 -2
  109. package/src/lib/virtual-fs/filesystem.ts +41 -16
  110. package/src/lib/virtual-fs/types.ts +19 -0
  111. package/src/lib/virtual-fs/virtual-fs.test.ts +204 -1
  112. package/src/tools/read-file/handler.test.ts +83 -0
  113. package/src/workflow.ts +3 -0
  114. package/tsup.config.ts +0 -4
  115. package/dist/adapters/sandbox/bedrock/index.cjs +0 -457
  116. package/dist/adapters/sandbox/bedrock/index.cjs.map +0 -1
  117. package/dist/adapters/sandbox/bedrock/index.d.cts +0 -25
  118. package/dist/adapters/sandbox/bedrock/index.d.ts +0 -25
  119. package/dist/adapters/sandbox/bedrock/index.js +0 -454
  120. package/dist/adapters/sandbox/bedrock/index.js.map +0 -1
  121. package/dist/adapters/sandbox/bedrock/workflow.cjs +0 -36
  122. package/dist/adapters/sandbox/bedrock/workflow.cjs.map +0 -1
  123. package/dist/adapters/sandbox/bedrock/workflow.d.cts +0 -29
  124. package/dist/adapters/sandbox/bedrock/workflow.d.ts +0 -29
  125. package/dist/adapters/sandbox/bedrock/workflow.js +0 -34
  126. package/dist/adapters/sandbox/bedrock/workflow.js.map +0 -1
  127. package/dist/types-DAsQ21Rt.d.ts +0 -74
  128. package/dist/types-lm8tMNJQ.d.cts +0 -74
  129. package/dist/types-yx0LzPGn.d.cts +0 -173
  130. package/dist/types-yx0LzPGn.d.ts +0 -173
  131. package/src/adapters/sandbox/bedrock/filesystem.ts +0 -340
  132. package/src/adapters/sandbox/bedrock/index.ts +0 -274
  133. package/src/adapters/sandbox/bedrock/proxy.ts +0 -59
  134. package/src/adapters/sandbox/bedrock/types.ts +0 -24
package/dist/index.cjs CHANGED
@@ -13,6 +13,16 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
13
13
  var z14__default = /*#__PURE__*/_interopDefault(z14);
14
14
 
15
15
  // src/lib/session/session.ts
16
+
17
+ // src/lib/session/types.ts
18
+ function resolveSessionLifecycle(init, shutdown) {
19
+ const resolvedInit = init ?? { mode: "new" };
20
+ const resolvedShutdown = shutdown ?? "destroy";
21
+ return {
22
+ mode: resolvedInit.mode,
23
+ shutdown: resolvedShutdown
24
+ };
25
+ }
16
26
  function createToolRouter(options) {
17
27
  const { appendToolResult } = options;
18
28
  const toolMap = /* @__PURE__ */ new Map();
@@ -441,6 +451,37 @@ function createSubagentTool(subagents) {
441
451
  schema
442
452
  };
443
453
  }
454
+
455
+ // src/lib/subagent/types.ts
456
+ function isSurvivalShutdown(s) {
457
+ return s === "pause" || s === "pause-until-parent-close" || s === "keep" || s === "keep-until-parent-close";
458
+ }
459
+ function resolveSubagentLifecycle(cfg, isLazyCreator) {
460
+ if (cfg.source !== "own") {
461
+ return {
462
+ shutdownOverride: cfg.shutdown,
463
+ mustSurvive: false
464
+ };
465
+ }
466
+ if (cfg.continuation === "snapshot") {
467
+ return {
468
+ shutdownOverride: "snapshot",
469
+ mustSurvive: false
470
+ };
471
+ }
472
+ const isLazy = cfg.init === "once";
473
+ const mustSurvive = isLazyCreator || cfg.continuation === "continue" || isLazy && cfg.continuation === "fork";
474
+ if (!mustSurvive) {
475
+ return { shutdownOverride: cfg.shutdown, mustSurvive: false };
476
+ }
477
+ if (isSurvivalShutdown(cfg.shutdown)) {
478
+ return { shutdownOverride: cfg.shutdown, mustSurvive };
479
+ }
480
+ return {
481
+ shutdownOverride: isLazyCreator ? "pause-until-parent-close" : "pause",
482
+ mustSurvive
483
+ };
484
+ }
444
485
  var childSandboxReadySignal = workflow.defineSignal("childSandboxReady");
445
486
 
446
487
  // src/lib/subagent/handler.ts
@@ -466,10 +507,18 @@ function resolveSandboxConfig(config) {
466
507
  }
467
508
  function createSubagentHandler(subagents) {
468
509
  const { taskQueue: parentTaskQueue } = workflow.workflowInfo();
469
- const agentSandboxOps = /* @__PURE__ */ new Map();
510
+ const agentDestroyOps = /* @__PURE__ */ new Map();
511
+ const agentDeleteSnapshotOps = /* @__PURE__ */ new Map();
470
512
  for (const cfg of subagents) {
471
- if (cfg.sandbox && cfg.sandbox !== "none") {
472
- agentSandboxOps.set(cfg.agentName, cfg.sandbox.proxy(cfg.agentName));
513
+ const cfgSandbox = cfg.sandbox;
514
+ if (!cfgSandbox || cfgSandbox === "none") continue;
515
+ if (cfgSandbox.continuation === "snapshot") {
516
+ const proxy = cfgSandbox.proxy(cfg.agentName);
517
+ agentDestroyOps.set(cfg.agentName, proxy);
518
+ agentDeleteSnapshotOps.set(cfg.agentName, proxy);
519
+ } else {
520
+ const proxy = cfgSandbox.proxy(cfg.agentName);
521
+ agentDestroyOps.set(cfg.agentName, proxy);
473
522
  }
474
523
  }
475
524
  const pendingDestroys = /* @__PURE__ */ new Map();
@@ -508,7 +557,7 @@ function createSubagentHandler(subagents) {
508
557
  const childWorkflowId = `${args.subagent}-${getShortId()}`;
509
558
  const { sandboxId: parentSandboxId } = context;
510
559
  const sandboxCfg = resolveSandboxConfig(config.sandbox);
511
- if (sandboxCfg.source !== "none" && !agentSandboxOps.has(config.agentName)) {
560
+ if (sandboxCfg.source !== "none" && !agentDestroyOps.has(config.agentName)) {
512
561
  throw workflow.ApplicationFailure.create({
513
562
  message: `Subagent "${config.agentName}" uses a sandbox but no \`sandbox.proxy\` is configured on its SubagentConfig`,
514
563
  nonRetryable: true
@@ -576,7 +625,6 @@ function createSubagentHandler(subagents) {
576
625
  if (baseSnap) {
577
626
  sandbox = { mode: "from-snapshot", snapshot: baseSnap };
578
627
  }
579
- sandboxShutdownOverride = "snapshot";
580
628
  } else if (sandboxCfg.source === "own") {
581
629
  const isLazy = sandboxCfg.init === "once";
582
630
  let baseSandboxId;
@@ -612,12 +660,10 @@ function createSubagentHandler(subagents) {
612
660
  sandboxId: baseSandboxId
613
661
  };
614
662
  }
615
- const userShutdown = sandboxCfg.shutdown;
616
- const alreadySurvives = userShutdown === "pause-until-parent-close" || userShutdown === "keep-until-parent-close" || userShutdown === "pause" || userShutdown === "keep";
617
- const mustSurvive = isLazyCreator || sandboxCfg.continuation === "continue" || isLazy && sandboxCfg.continuation === "fork";
618
- if (mustSurvive && !alreadySurvives) {
619
- sandboxShutdownOverride = isLazyCreator ? "pause-until-parent-close" : "pause";
620
- }
663
+ }
664
+ {
665
+ const lifecycle = resolveSubagentLifecycle(sandboxCfg, isLazyCreator);
666
+ sandboxShutdownOverride = lifecycle.shutdownOverride;
621
667
  }
622
668
  const workflowInput = {
623
669
  ...thread && { thread },
@@ -762,7 +808,7 @@ function createSubagentHandler(subagents) {
762
808
  pendingDestroys.clear();
763
809
  await Promise.all(
764
810
  entries.map(async ({ agentName, sandboxId }) => {
765
- const ops = agentSandboxOps.get(agentName);
811
+ const ops = agentDestroyOps.get(agentName);
766
812
  if (!ops) {
767
813
  workflow.log.warn(
768
814
  "Skipping sandbox destroy \u2014 no sandbox.proxy registered for agent",
@@ -792,7 +838,7 @@ function createSubagentHandler(subagents) {
792
838
  persistentBaseSnapshot.clear();
793
839
  await Promise.all(
794
840
  tagged.map(async ({ agentName, snapshot }) => {
795
- const ops = agentSandboxOps.get(agentName);
841
+ const ops = agentDeleteSnapshotOps.get(agentName);
796
842
  if (!ops) {
797
843
  workflow.log.warn(
798
844
  "Skipping snapshot delete \u2014 no sandbox.proxy registered for agent",
@@ -944,30 +990,31 @@ function collectSkillFiles(skills) {
944
990
  }
945
991
  return files;
946
992
  }
947
- async function createSession({
948
- agentName,
949
- maxTurns = 50,
950
- metadata = {},
951
- runAgent,
952
- threadOps,
953
- buildContextMessage,
954
- subagents,
955
- skills,
956
- tools = {},
957
- processToolsInParallel = true,
958
- hooks = {},
959
- appendSystemPrompt = true,
960
- waitForInputTimeout = "48h",
961
- threadKey,
962
- sandboxOps,
963
- thread: threadInit,
964
- sandbox: sandboxInit,
965
- sandboxShutdown = "destroy",
966
- onSandboxReady,
967
- onSessionExit,
968
- virtualFs: virtualFsConfig,
969
- virtualFsOps
970
- }) {
993
+ async function createSession(config) {
994
+ const {
995
+ agentName,
996
+ maxTurns = 50,
997
+ metadata = {},
998
+ runAgent,
999
+ threadOps,
1000
+ buildContextMessage,
1001
+ subagents,
1002
+ skills,
1003
+ tools = {},
1004
+ processToolsInParallel = true,
1005
+ hooks = {},
1006
+ appendSystemPrompt = true,
1007
+ threadKey,
1008
+ sandboxOps,
1009
+ thread: threadInit,
1010
+ sandbox: sandboxInit,
1011
+ sandboxShutdown,
1012
+ onSandboxReady,
1013
+ onSessionExit,
1014
+ virtualFs: virtualFsConfig,
1015
+ virtualFsOps
1016
+ } = config;
1017
+ const wideOps = () => sandboxOps;
971
1018
  const threadMode = threadInit?.mode ?? "new";
972
1019
  let threadId;
973
1020
  let sourceThreadId;
@@ -1051,7 +1098,12 @@ async function createSession({
1051
1098
  stateManager.run();
1052
1099
  }
1053
1100
  );
1054
- const sandboxMode = sandboxInit?.mode;
1101
+ const lifecycle = resolveSessionLifecycle(
1102
+ sandboxInit,
1103
+ sandboxShutdown
1104
+ );
1105
+ const sandboxMode = lifecycle.mode;
1106
+ const resolvedShutdown = lifecycle.shutdown;
1055
1107
  let sandboxId;
1056
1108
  let sandboxOwned = false;
1057
1109
  let baseSnapshot;
@@ -1074,8 +1126,8 @@ async function createSession({
1074
1126
  });
1075
1127
  }
1076
1128
  sandboxId = sandboxInit.sandboxId;
1077
- if (sandboxShutdown === "pause-until-parent-close") {
1078
- await sandboxOps.resumeSandbox(sandboxId);
1129
+ if (resolvedShutdown === "pause-until-parent-close") {
1130
+ await wideOps().resumeSandbox(sandboxId);
1079
1131
  }
1080
1132
  sandboxOwned = true;
1081
1133
  } else if (sandboxMode === "fork") {
@@ -1086,7 +1138,7 @@ async function createSession({
1086
1138
  });
1087
1139
  }
1088
1140
  const forkInit = sandboxInit;
1089
- sandboxId = await sandboxOps.forkSandbox(
1141
+ sandboxId = await wideOps().forkSandbox(
1090
1142
  forkInit.sandboxId,
1091
1143
  forkInit.options
1092
1144
  );
@@ -1099,7 +1151,7 @@ async function createSession({
1099
1151
  });
1100
1152
  }
1101
1153
  const restoreInit = sandboxInit;
1102
- sandboxId = await sandboxOps.restoreSandbox(
1154
+ sandboxId = await wideOps().restoreSandbox(
1103
1155
  restoreInit.snapshot,
1104
1156
  restoreInit.options
1105
1157
  );
@@ -1115,8 +1167,8 @@ async function createSession({
1115
1167
  freshlyCreated = true;
1116
1168
  }
1117
1169
  }
1118
- if (sandboxId && sandboxOwned && freshlyCreated && sandboxShutdown === "snapshot" && sandboxOps) {
1119
- baseSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
1170
+ if (sandboxId && sandboxOwned && freshlyCreated && resolvedShutdown === "snapshot" && sandboxOps) {
1171
+ baseSnapshot = await wideOps().snapshotSandbox(sandboxId);
1120
1172
  }
1121
1173
  if (sandboxId && sandboxOwned && onSandboxReady) {
1122
1174
  onSandboxReady({
@@ -1140,12 +1192,23 @@ async function createSession({
1140
1192
  path,
1141
1193
  size: content.length,
1142
1194
  mtime: (/* @__PURE__ */ new Date()).toISOString(),
1143
- metadata: {}
1195
+ metadata: {},
1196
+ // Carry the content directly on the entry so any handler that
1197
+ // constructs a VirtualFileSystem from `fileTree` can read it
1198
+ // without needing to also wire up `inlineFiles` from state.
1199
+ inlineContent: content
1144
1200
  }))
1145
1201
  ] : result.fileTree;
1146
1202
  stateManager.mergeUpdate({
1147
1203
  fileTree,
1148
1204
  virtualFsCtx: virtualFsConfig.ctx,
1205
+ // `inlineFiles` is still the source of truth at read time:
1206
+ // VirtualFileSystem checks the inlineFiles map first and only
1207
+ // falls through to entry.inlineContent. Embedding the content on
1208
+ // the entry is the migration target so that handlers building a
1209
+ // VirtualFileSystem from `fileTree` alone (without forwarding
1210
+ // `inlineFiles` from state) can read skill resources. Until a
1211
+ // follow-up drops `inlineFiles`, both fields are populated.
1149
1212
  ...skillFiles && { inlineFiles: skillFiles }
1150
1213
  });
1151
1214
  }
@@ -1271,18 +1334,6 @@ async function createSession({
1271
1334
  continue;
1272
1335
  }
1273
1336
  assistantId = void 0;
1274
- if (stateManager.getStatus() === "WAITING_FOR_INPUT") {
1275
- const conditionMet = await workflow.condition(
1276
- () => stateManager.getStatus() === "RUNNING",
1277
- waitForInputTimeout
1278
- );
1279
- if (!conditionMet) {
1280
- stateManager.cancel();
1281
- exitReason = "cancelled";
1282
- await workflow.condition(() => false, "2s");
1283
- break;
1284
- }
1285
- }
1286
1337
  }
1287
1338
  if (stateManager.getTurns() >= maxTurns && stateManager.isRunning()) {
1288
1339
  exitReason = "max_turns";
@@ -1318,19 +1369,19 @@ async function createSession({
1318
1369
  }
1319
1370
  await callSessionEnd(exitReason, stateManager.getTurns());
1320
1371
  if (sandboxOwned && sandboxId && sandboxOps) {
1321
- switch (sandboxShutdown) {
1372
+ switch (resolvedShutdown) {
1322
1373
  case "destroy":
1323
1374
  await sandboxOps.destroySandbox(sandboxId);
1324
1375
  break;
1325
1376
  case "pause":
1326
1377
  case "pause-until-parent-close":
1327
- await sandboxOps.pauseSandbox(sandboxId);
1378
+ await wideOps().pauseSandbox(sandboxId);
1328
1379
  break;
1329
1380
  case "keep":
1330
1381
  case "keep-until-parent-close":
1331
1382
  break;
1332
1383
  case "snapshot":
1333
- exitSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
1384
+ exitSnapshot = await wideOps().snapshotSandbox(sandboxId);
1334
1385
  await sandboxOps.destroySandbox(sandboxId);
1335
1386
  break;
1336
1387
  }
@@ -1356,7 +1407,8 @@ async function createSession({
1356
1407
  onSessionExit({
1357
1408
  ...sandboxId && { sandboxId },
1358
1409
  ...exitSnapshot && { snapshot: exitSnapshot },
1359
- threadId
1410
+ threadId,
1411
+ usage: stateManager.getTotalUsage()
1360
1412
  });
1361
1413
  }
1362
1414
  return {
@@ -1644,9 +1696,24 @@ function withAutoAppend(threadHandler, handler) {
1644
1696
  };
1645
1697
  };
1646
1698
  }
1699
+ var SandboxNotSupportedError = class extends common.ApplicationFailure {
1700
+ constructor(operation) {
1701
+ super(
1702
+ `Sandbox does not support: ${operation}`,
1703
+ "SandboxNotSupportedError",
1704
+ true
1705
+ );
1706
+ }
1707
+ };
1708
+ var SandboxNotFoundError = class extends common.ApplicationFailure {
1709
+ constructor(sandboxId) {
1710
+ super(`Sandbox not found: ${sandboxId}`, "SandboxNotFoundError", true);
1711
+ }
1712
+ };
1647
1713
 
1648
1714
  // src/lib/tool-router/with-sandbox.ts
1649
- function withSandbox(manager, handler) {
1715
+ function withSandbox(manager, handler, options) {
1716
+ const translateSandboxNotFound = options?.translateSandboxNotFound ?? false;
1650
1717
  return async (args, context) => {
1651
1718
  if (!context.sandboxId) {
1652
1719
  return {
@@ -1654,7 +1721,18 @@ function withSandbox(manager, handler) {
1654
1721
  data: null
1655
1722
  };
1656
1723
  }
1657
- const sandbox = await manager.getSandbox(context.sandboxId);
1724
+ let sandbox;
1725
+ try {
1726
+ sandbox = await manager.getSandbox(context.sandboxId);
1727
+ } catch (err) {
1728
+ if (translateSandboxNotFound && err instanceof SandboxNotFoundError) {
1729
+ return {
1730
+ toolResponse: options?.sandboxNotFoundToolResponse ?? `Error: the execution environment for the ${context.toolName} tool is no longer available, so this tool call could not be completed.`,
1731
+ data: null
1732
+ };
1733
+ }
1734
+ throw err;
1735
+ }
1658
1736
  return handler(args, { ...context, sandbox, sandboxId: context.sandboxId });
1659
1737
  };
1660
1738
  }
@@ -1686,6 +1764,7 @@ function defineSubagentWorkflow(config, fn) {
1686
1764
  let capturedSnapshot;
1687
1765
  let capturedBaseSnapshot;
1688
1766
  let capturedThreadId;
1767
+ let capturedUsage;
1689
1768
  const sessionInput = {
1690
1769
  agentName: config.name,
1691
1770
  sandboxShutdown: effectiveShutdown,
@@ -1702,10 +1781,17 @@ function defineSubagentWorkflow(config, fn) {
1702
1781
  });
1703
1782
  }
1704
1783
  },
1705
- onSessionExit: ({ sandboxId, snapshot, threadId }) => {
1784
+ onSessionExit: ({ sandboxId, snapshot, threadId, usage }) => {
1706
1785
  capturedSandboxId = sandboxId;
1707
1786
  capturedSnapshot = snapshot;
1708
1787
  capturedThreadId = threadId;
1788
+ capturedUsage = {
1789
+ inputTokens: usage.totalInputTokens,
1790
+ outputTokens: usage.totalOutputTokens,
1791
+ cachedWriteTokens: usage.totalCachedWriteTokens,
1792
+ cachedReadTokens: usage.totalCachedReadTokens,
1793
+ reasonTokens: usage.totalReasonTokens
1794
+ };
1709
1795
  }
1710
1796
  };
1711
1797
  const result = await fn(prompt, sessionInput, context ?? {});
@@ -1716,7 +1802,8 @@ function defineSubagentWorkflow(config, fn) {
1716
1802
  ...capturedSnapshot !== void 0 && { snapshot: capturedSnapshot },
1717
1803
  ...capturedBaseSnapshot !== void 0 && {
1718
1804
  baseSnapshot: capturedBaseSnapshot
1719
- }
1805
+ },
1806
+ ...capturedUsage !== void 0 && { usage: capturedUsage }
1720
1807
  };
1721
1808
  };
1722
1809
  Object.defineProperty(workflow$1, "name", { value: config.name });
@@ -1800,20 +1887,6 @@ function proxyRunAgent(scope, options) {
1800
1887
  );
1801
1888
  return acts[name];
1802
1889
  }
1803
- var SandboxNotSupportedError = class extends common.ApplicationFailure {
1804
- constructor(operation) {
1805
- super(
1806
- `Sandbox does not support: ${operation}`,
1807
- "SandboxNotSupportedError",
1808
- true
1809
- );
1810
- }
1811
- };
1812
- var SandboxNotFoundError = class extends common.ApplicationFailure {
1813
- constructor(sandboxId) {
1814
- super(`Sandbox not found: ${sandboxId}`, "SandboxNotFoundError", true);
1815
- }
1816
- };
1817
1890
 
1818
1891
  // src/lib/virtual-fs/mutations.ts
1819
1892
  function applyVirtualTreeMutations(stateManager, mutations) {
@@ -2656,6 +2729,9 @@ function getActivityContext() {
2656
2729
  }
2657
2730
  async function queryParentWorkflowState(client) {
2658
2731
  const { workflowExecution } = activity.Context.current().info;
2732
+ if (!workflowExecution) {
2733
+ throw new Error("No workflow execution found");
2734
+ }
2659
2735
  const handle = client.getHandle(
2660
2736
  workflowExecution.workflowId,
2661
2737
  workflowExecution.runId
@@ -2679,13 +2755,56 @@ function withParentWorkflowState(client, handler) {
2679
2755
  }
2680
2756
 
2681
2757
  // src/lib/sandbox/manager.ts
2758
+ var CAP_METHOD_TO_CAPABILITY = [
2759
+ { method: "pause", capability: "pause" },
2760
+ { method: "resume", capability: "resume" },
2761
+ { method: "snapshot", capability: "snapshot" },
2762
+ { method: "deleteSnapshot", capability: "snapshot" },
2763
+ { method: "restore", capability: "restore" },
2764
+ { method: "fork", capability: "fork" }
2765
+ ];
2682
2766
  var SandboxManager = class {
2683
2767
  constructor(provider, options) {
2684
2768
  this.provider = provider;
2685
2769
  this.hooks = options?.hooks ?? {};
2770
+ this.assertCapabilityRuntimeConsistency();
2686
2771
  }
2687
2772
  provider;
2688
2773
  hooks;
2774
+ /**
2775
+ * Verifies that the provider's runtime `supportedCapabilities` set is
2776
+ * consistent with the gated methods physically present on the provider.
2777
+ *
2778
+ * Belt-and-suspenders complement to the type-level
2779
+ * `ReadonlySet<TCaps & SandboxCapability>` constraint: TypeScript can
2780
+ * prevent the runtime set from containing capabilities not declared in
2781
+ * `TCaps`, but it cannot detect a provider that **declares** a cap in
2782
+ * `TCaps` and forgets to include it in the runtime set (or that ships
2783
+ * a method without listing its cap). Both shapes silently break
2784
+ * activity registration, so we trip a loud failure at construction
2785
+ * time instead.
2786
+ *
2787
+ * Adapters that derive both surfaces from a single `as const`
2788
+ * capability array (the recommended pattern) pass this check by
2789
+ * construction.
2790
+ */
2791
+ assertCapabilityRuntimeConsistency() {
2792
+ const supported = this.provider.supportedCapabilities;
2793
+ for (const { method, capability } of CAP_METHOD_TO_CAPABILITY) {
2794
+ const hasMethod = typeof this.provider[method] === "function";
2795
+ const declaresCap = supported.has(capability);
2796
+ if (hasMethod && !declaresCap) {
2797
+ throw new Error(
2798
+ `Sandbox provider "${this.provider.id}" implements ${method}() but does not list "${capability}" in supportedCapabilities. Add the capability to the provider's runtime set so activities for it can be registered.`
2799
+ );
2800
+ }
2801
+ if (declaresCap && !hasMethod) {
2802
+ throw new Error(
2803
+ `Sandbox provider "${this.provider.id}" lists "${capability}" in supportedCapabilities but does not implement ${method}(). Either add the method to the provider or remove the capability from supportedCapabilities.`
2804
+ );
2805
+ }
2806
+ }
2807
+ }
2689
2808
  async create(options, ctx) {
2690
2809
  let providerOptions = options;
2691
2810
  if (this.hooks.onPreCreate) {
@@ -2723,26 +2842,61 @@ var SandboxManager = class {
2723
2842
  async destroy(id) {
2724
2843
  await this.provider.destroy(id);
2725
2844
  }
2845
+ /**
2846
+ * Capability-gated lifecycle methods on the underlying provider.
2847
+ *
2848
+ * These manager methods always exist at runtime; calling one whose
2849
+ * capability is absent from the provider's `supportedCapabilities`
2850
+ * throws an error. The activities returned from
2851
+ * {@link SandboxManager.createActivities} are gated at the type level
2852
+ * via `TCaps`, which is where compile-time safety is enforced.
2853
+ */
2726
2854
  async pause(id, ttlSeconds) {
2727
- await this.provider.pause(id, ttlSeconds);
2855
+ const fn = this.providerMethod("pause");
2856
+ if (!fn) throw this.unsupported("pause");
2857
+ await fn.call(this.provider, id, ttlSeconds);
2728
2858
  }
2729
2859
  async resume(id) {
2730
- await this.provider.resume(id);
2860
+ const fn = this.providerMethod("resume");
2861
+ if (!fn) throw this.unsupported("resume");
2862
+ await fn.call(this.provider, id);
2731
2863
  }
2732
2864
  async snapshot(id, options) {
2733
- return this.provider.snapshot(id, options);
2865
+ const fn = this.providerMethod("snapshot");
2866
+ if (!fn) throw this.unsupported("snapshot");
2867
+ return fn.call(this.provider, id, options);
2734
2868
  }
2735
2869
  async restore(snapshot, options) {
2736
- const sandbox = await this.provider.restore(snapshot, options);
2870
+ const fn = this.providerMethod("restore");
2871
+ if (!fn) throw this.unsupported("restore");
2872
+ const sandbox = await fn.call(this.provider, snapshot, options);
2737
2873
  return sandbox.id;
2738
2874
  }
2739
2875
  async deleteSnapshot(snapshot) {
2740
- await this.provider.deleteSnapshot(snapshot);
2876
+ const fn = this.providerMethod("deleteSnapshot");
2877
+ if (!fn) throw this.unsupported("deleteSnapshot");
2878
+ await fn.call(this.provider, snapshot);
2741
2879
  }
2742
2880
  async fork(sandboxId, options) {
2743
- const sandbox = await this.provider.fork(sandboxId, options);
2881
+ const fn = this.providerMethod("fork");
2882
+ if (!fn) throw this.unsupported("fork");
2883
+ const sandbox = await fn.call(this.provider, sandboxId, options);
2744
2884
  return sandbox.id;
2745
2885
  }
2886
+ providerMethod(name) {
2887
+ const value = this.provider[name];
2888
+ return typeof value === "function" ? value : void 0;
2889
+ }
2890
+ /**
2891
+ * Constructs the structured error thrown when an unsupported lifecycle
2892
+ * method is invoked through the manager. Uses the public
2893
+ * {@link SandboxNotSupportedError} symbol so consumers that catch on
2894
+ * `instanceof SandboxNotSupportedError` (the documented compatibility
2895
+ * path) keep matching after the refactor.
2896
+ */
2897
+ unsupported(name) {
2898
+ return new SandboxNotSupportedError(name);
2899
+ }
2746
2900
  /**
2747
2901
  * Returns Temporal activity functions with prefixed names.
2748
2902
  *
@@ -2750,6 +2904,11 @@ var SandboxManager = class {
2750
2904
  * to pass the workflow/scope name. Use the matching `proxy*SandboxOps()`
2751
2905
  * helper from the adapter's `/workflow` entrypoint on the workflow side.
2752
2906
  *
2907
+ * Activities are only registered for capabilities the provider declares
2908
+ * via {@link SandboxProvider.supportedCapabilities}: methods omitted
2909
+ * from the cap set are not wrapped, and the returned object's type
2910
+ * omits the corresponding keys.
2911
+ *
2753
2912
  * @param scope - Workflow name (appended to the provider id)
2754
2913
  *
2755
2914
  * @example
@@ -2760,40 +2919,55 @@ var SandboxManager = class {
2760
2919
  *
2761
2920
  * const dmgr = new SandboxManager(new DaytonaSandboxProvider(config));
2762
2921
  * dmgr.createActivities("CodingAgent");
2763
- * // registers: daytonaCodingAgentCreateSandbox,
2922
+ * // registers: daytonaCodingAgentCreateSandbox, daytonaCodingAgentDestroySandbox
2923
+ * // (snapshot/restore/fork/pause/resume omitted — Daytona doesn't declare them)
2764
2924
  * ```
2765
2925
  */
2766
2926
  createActivities(scope) {
2767
2927
  const prefix = `${this.provider.id}${scope.charAt(0).toUpperCase()}${scope.slice(1)}`;
2928
+ const cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
2929
+ const supported = this.provider.supportedCapabilities;
2768
2930
  const ops = {
2769
2931
  createSandbox: async (options, ctx) => {
2770
2932
  return this.create(options, ctx);
2771
2933
  },
2772
2934
  destroySandbox: async (sandboxId) => {
2773
2935
  await this.destroy(sandboxId);
2774
- },
2775
- pauseSandbox: async (sandboxId, ttlSeconds) => {
2936
+ }
2937
+ };
2938
+ if (supported.has("pause")) {
2939
+ ops.pauseSandbox = async (sandboxId, ttlSeconds) => {
2776
2940
  await this.pause(sandboxId, ttlSeconds);
2777
- },
2778
- resumeSandbox: async (sandboxId) => {
2941
+ };
2942
+ }
2943
+ if (supported.has("resume")) {
2944
+ ops.resumeSandbox = async (sandboxId) => {
2779
2945
  await this.resume(sandboxId);
2780
- },
2781
- snapshotSandbox: async (sandboxId, options) => {
2946
+ };
2947
+ }
2948
+ if (supported.has("snapshot")) {
2949
+ ops.snapshotSandbox = async (sandboxId, options) => {
2782
2950
  return this.snapshot(sandboxId, options);
2783
- },
2784
- restoreSandbox: async (snapshot, options) => {
2785
- return this.restore(snapshot, options);
2786
- },
2787
- deleteSandboxSnapshot: async (snapshot) => {
2951
+ };
2952
+ ops.deleteSandboxSnapshot = async (snapshot) => {
2788
2953
  await this.deleteSnapshot(snapshot);
2789
- },
2790
- forkSandbox: async (sandboxId, options) => {
2954
+ };
2955
+ }
2956
+ if (supported.has("restore")) {
2957
+ ops.restoreSandbox = async (snapshot, options) => {
2958
+ return this.restore(snapshot, options);
2959
+ };
2960
+ }
2961
+ if (supported.has("fork")) {
2962
+ ops.forkSandbox = async (sandboxId, options) => {
2791
2963
  return this.fork(sandboxId, options);
2792
- }
2793
- };
2794
- const cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
2964
+ };
2965
+ }
2966
+ const entries = Object.entries(ops).filter(
2967
+ ([, v]) => typeof v === "function"
2968
+ );
2795
2969
  return Object.fromEntries(
2796
- Object.entries(ops).map(([k, v]) => [`${prefix}${cap(k)}`, v])
2970
+ entries.map(([k, v]) => [`${prefix}${cap(k)}`, v])
2797
2971
  );
2798
2972
  }
2799
2973
  };
@@ -2926,7 +3100,7 @@ var VirtualFileSystem = class {
2926
3100
  if (inline !== void 0) return inline;
2927
3101
  const entry = this.entries.get(norm);
2928
3102
  if (!entry) throw new Error(`ENOENT: no such file: ${path}`);
2929
- return this.resolver.readFile(entry.id, this.ctx, entry.metadata);
3103
+ return this.readEntryContent(entry);
2930
3104
  }
2931
3105
  async readFileBuffer(path) {
2932
3106
  const norm = normalisePath(path, this.workspaceBase);
@@ -2934,8 +3108,23 @@ var VirtualFileSystem = class {
2934
3108
  if (inline !== void 0) return new TextEncoder().encode(inline);
2935
3109
  const entry = this.entries.get(norm);
2936
3110
  if (!entry) throw new Error(`ENOENT: no such file: ${path}`);
3111
+ if (entry.inlineContent !== void 0) {
3112
+ return new TextEncoder().encode(entry.inlineContent);
3113
+ }
2937
3114
  return this.resolver.readFileBuffer(entry.id, this.ctx, entry.metadata);
2938
3115
  }
3116
+ /**
3117
+ * Resolve the string content for an entry, preferring inline content
3118
+ * carried on the entry itself before consulting the resolver. Used by
3119
+ * `readFile`, `appendFile`, and `cp` so all read paths agree on the
3120
+ * lookup precedence.
3121
+ */
3122
+ readEntryContent(entry) {
3123
+ if (entry.inlineContent !== void 0) {
3124
+ return Promise.resolve(entry.inlineContent);
3125
+ }
3126
+ return this.resolver.readFile(entry.id, this.ctx, entry.metadata);
3127
+ }
2939
3128
  // --------------------------------------------------------------------------
2940
3129
  // Metadata operations — pure, resolved from the tree
2941
3130
  // --------------------------------------------------------------------------
@@ -3007,6 +3196,11 @@ var VirtualFileSystem = class {
3007
3196
  const norm = normalisePath(path, this.workspaceBase);
3008
3197
  const existing = this.entries.get(norm);
3009
3198
  if (existing) {
3199
+ if (existing.inlineContent !== void 0) {
3200
+ throw new Error(
3201
+ `EROFS: cannot write to inline (read-only) entry: ${path}`
3202
+ );
3203
+ }
3010
3204
  await this.resolver.writeFile(
3011
3205
  existing.id,
3012
3206
  content,
@@ -3035,11 +3229,12 @@ var VirtualFileSystem = class {
3035
3229
  if (!existing) {
3036
3230
  return this.writeFile(path, content);
3037
3231
  }
3038
- const current = await this.resolver.readFile(
3039
- existing.id,
3040
- this.ctx,
3041
- existing.metadata
3042
- );
3232
+ if (existing.inlineContent !== void 0) {
3233
+ throw new Error(
3234
+ `EROFS: cannot append to inline (read-only) entry: ${path}`
3235
+ );
3236
+ }
3237
+ const current = await this.readEntryContent(existing);
3043
3238
  const appended = typeof content === "string" ? current + content : current + new TextDecoder().decode(content);
3044
3239
  await this.resolver.writeFile(
3045
3240
  existing.id,
@@ -3074,6 +3269,11 @@ var VirtualFileSystem = class {
3074
3269
  const norm = normalisePath(path, this.workspaceBase);
3075
3270
  const entry = this.entries.get(norm);
3076
3271
  if (entry) {
3272
+ if (entry.inlineContent !== void 0) {
3273
+ throw new Error(
3274
+ `EROFS: cannot remove inline (read-only) entry: ${path}`
3275
+ );
3276
+ }
3077
3277
  await this.resolver.deleteFile(entry.id, this.ctx, entry.metadata);
3078
3278
  this.entries.delete(norm);
3079
3279
  this.mutations.push({ type: "remove", path: norm });
@@ -3086,6 +3286,11 @@ var VirtualFileSystem = class {
3086
3286
  const prefix = norm === "/" ? "/" : norm + "/";
3087
3287
  for (const [p, e] of this.entries) {
3088
3288
  if (p.startsWith(prefix)) {
3289
+ if (e.inlineContent !== void 0) {
3290
+ throw new Error(
3291
+ `EROFS: cannot remove inline (read-only) entry: ${p}`
3292
+ );
3293
+ }
3089
3294
  await this.resolver.deleteFile(e.id, this.ctx, e.metadata);
3090
3295
  this.entries.delete(p);
3091
3296
  this.mutations.push({ type: "remove", path: p });
@@ -3106,11 +3311,7 @@ var VirtualFileSystem = class {
3106
3311
  const normDest = normalisePath(dest, this.workspaceBase);
3107
3312
  const entry = this.entries.get(normSrc);
3108
3313
  if (entry) {
3109
- const content = await this.resolver.readFile(
3110
- entry.id,
3111
- this.ctx,
3112
- entry.metadata
3113
- );
3314
+ const content = await this.readEntryContent(entry);
3114
3315
  await this.writeFile(normDest, content);
3115
3316
  return;
3116
3317
  }
@@ -3124,11 +3325,7 @@ var VirtualFileSystem = class {
3124
3325
  for (const [p, e] of this.entries) {
3125
3326
  if (p.startsWith(prefix)) {
3126
3327
  const relative = p.slice(normSrc.length);
3127
- const content = await this.resolver.readFile(
3128
- e.id,
3129
- this.ctx,
3130
- e.metadata
3131
- );
3328
+ const content = await this.readEntryContent(e);
3132
3329
  await this.writeFile(normDest + relative, content);
3133
3330
  }
3134
3331
  }