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.js CHANGED
@@ -1,4 +1,4 @@
1
- import { defineSignal, CancellationScope, isCancellation, uuid4, setHandler, defineUpdate, ApplicationFailure, log, condition, defineQuery, proxySinks, workflowInfo, proxyActivities, getExternalWorkflowHandle, executeChild } from '@temporalio/workflow';
1
+ import { defineSignal, CancellationScope, isCancellation, uuid4, setHandler, defineUpdate, ApplicationFailure, log, defineQuery, condition, proxySinks, workflowInfo, proxyActivities, getExternalWorkflowHandle, executeChild } from '@temporalio/workflow';
2
2
  import z14, { z } from 'zod';
3
3
  import { randomUUID, randomFillSync } from 'crypto';
4
4
  import { ApplicationFailure as ApplicationFailure$1 } from '@temporalio/common';
@@ -7,6 +7,16 @@ import { Context } from '@temporalio/activity';
7
7
  import { promises } from 'fs';
8
8
 
9
9
  // src/lib/session/session.ts
10
+
11
+ // src/lib/session/types.ts
12
+ function resolveSessionLifecycle(init, shutdown) {
13
+ const resolvedInit = init ?? { mode: "new" };
14
+ const resolvedShutdown = shutdown ?? "destroy";
15
+ return {
16
+ mode: resolvedInit.mode,
17
+ shutdown: resolvedShutdown
18
+ };
19
+ }
10
20
  function createToolRouter(options) {
11
21
  const { appendToolResult } = options;
12
22
  const toolMap = /* @__PURE__ */ new Map();
@@ -435,6 +445,37 @@ function createSubagentTool(subagents) {
435
445
  schema
436
446
  };
437
447
  }
448
+
449
+ // src/lib/subagent/types.ts
450
+ function isSurvivalShutdown(s) {
451
+ return s === "pause" || s === "pause-until-parent-close" || s === "keep" || s === "keep-until-parent-close";
452
+ }
453
+ function resolveSubagentLifecycle(cfg, isLazyCreator) {
454
+ if (cfg.source !== "own") {
455
+ return {
456
+ shutdownOverride: cfg.shutdown,
457
+ mustSurvive: false
458
+ };
459
+ }
460
+ if (cfg.continuation === "snapshot") {
461
+ return {
462
+ shutdownOverride: "snapshot",
463
+ mustSurvive: false
464
+ };
465
+ }
466
+ const isLazy = cfg.init === "once";
467
+ const mustSurvive = isLazyCreator || cfg.continuation === "continue" || isLazy && cfg.continuation === "fork";
468
+ if (!mustSurvive) {
469
+ return { shutdownOverride: cfg.shutdown, mustSurvive: false };
470
+ }
471
+ if (isSurvivalShutdown(cfg.shutdown)) {
472
+ return { shutdownOverride: cfg.shutdown, mustSurvive };
473
+ }
474
+ return {
475
+ shutdownOverride: isLazyCreator ? "pause-until-parent-close" : "pause",
476
+ mustSurvive
477
+ };
478
+ }
438
479
  var childSandboxReadySignal = defineSignal("childSandboxReady");
439
480
 
440
481
  // src/lib/subagent/handler.ts
@@ -460,10 +501,18 @@ function resolveSandboxConfig(config) {
460
501
  }
461
502
  function createSubagentHandler(subagents) {
462
503
  const { taskQueue: parentTaskQueue } = workflowInfo();
463
- const agentSandboxOps = /* @__PURE__ */ new Map();
504
+ const agentDestroyOps = /* @__PURE__ */ new Map();
505
+ const agentDeleteSnapshotOps = /* @__PURE__ */ new Map();
464
506
  for (const cfg of subagents) {
465
- if (cfg.sandbox && cfg.sandbox !== "none") {
466
- agentSandboxOps.set(cfg.agentName, cfg.sandbox.proxy(cfg.agentName));
507
+ const cfgSandbox = cfg.sandbox;
508
+ if (!cfgSandbox || cfgSandbox === "none") continue;
509
+ if (cfgSandbox.continuation === "snapshot") {
510
+ const proxy = cfgSandbox.proxy(cfg.agentName);
511
+ agentDestroyOps.set(cfg.agentName, proxy);
512
+ agentDeleteSnapshotOps.set(cfg.agentName, proxy);
513
+ } else {
514
+ const proxy = cfgSandbox.proxy(cfg.agentName);
515
+ agentDestroyOps.set(cfg.agentName, proxy);
467
516
  }
468
517
  }
469
518
  const pendingDestroys = /* @__PURE__ */ new Map();
@@ -502,7 +551,7 @@ function createSubagentHandler(subagents) {
502
551
  const childWorkflowId = `${args.subagent}-${getShortId()}`;
503
552
  const { sandboxId: parentSandboxId } = context;
504
553
  const sandboxCfg = resolveSandboxConfig(config.sandbox);
505
- if (sandboxCfg.source !== "none" && !agentSandboxOps.has(config.agentName)) {
554
+ if (sandboxCfg.source !== "none" && !agentDestroyOps.has(config.agentName)) {
506
555
  throw ApplicationFailure.create({
507
556
  message: `Subagent "${config.agentName}" uses a sandbox but no \`sandbox.proxy\` is configured on its SubagentConfig`,
508
557
  nonRetryable: true
@@ -570,7 +619,6 @@ function createSubagentHandler(subagents) {
570
619
  if (baseSnap) {
571
620
  sandbox = { mode: "from-snapshot", snapshot: baseSnap };
572
621
  }
573
- sandboxShutdownOverride = "snapshot";
574
622
  } else if (sandboxCfg.source === "own") {
575
623
  const isLazy = sandboxCfg.init === "once";
576
624
  let baseSandboxId;
@@ -606,12 +654,10 @@ function createSubagentHandler(subagents) {
606
654
  sandboxId: baseSandboxId
607
655
  };
608
656
  }
609
- const userShutdown = sandboxCfg.shutdown;
610
- const alreadySurvives = userShutdown === "pause-until-parent-close" || userShutdown === "keep-until-parent-close" || userShutdown === "pause" || userShutdown === "keep";
611
- const mustSurvive = isLazyCreator || sandboxCfg.continuation === "continue" || isLazy && sandboxCfg.continuation === "fork";
612
- if (mustSurvive && !alreadySurvives) {
613
- sandboxShutdownOverride = isLazyCreator ? "pause-until-parent-close" : "pause";
614
- }
657
+ }
658
+ {
659
+ const lifecycle = resolveSubagentLifecycle(sandboxCfg, isLazyCreator);
660
+ sandboxShutdownOverride = lifecycle.shutdownOverride;
615
661
  }
616
662
  const workflowInput = {
617
663
  ...thread && { thread },
@@ -756,7 +802,7 @@ function createSubagentHandler(subagents) {
756
802
  pendingDestroys.clear();
757
803
  await Promise.all(
758
804
  entries.map(async ({ agentName, sandboxId }) => {
759
- const ops = agentSandboxOps.get(agentName);
805
+ const ops = agentDestroyOps.get(agentName);
760
806
  if (!ops) {
761
807
  log.warn(
762
808
  "Skipping sandbox destroy \u2014 no sandbox.proxy registered for agent",
@@ -786,7 +832,7 @@ function createSubagentHandler(subagents) {
786
832
  persistentBaseSnapshot.clear();
787
833
  await Promise.all(
788
834
  tagged.map(async ({ agentName, snapshot }) => {
789
- const ops = agentSandboxOps.get(agentName);
835
+ const ops = agentDeleteSnapshotOps.get(agentName);
790
836
  if (!ops) {
791
837
  log.warn(
792
838
  "Skipping snapshot delete \u2014 no sandbox.proxy registered for agent",
@@ -938,30 +984,31 @@ function collectSkillFiles(skills) {
938
984
  }
939
985
  return files;
940
986
  }
941
- async function createSession({
942
- agentName,
943
- maxTurns = 50,
944
- metadata = {},
945
- runAgent,
946
- threadOps,
947
- buildContextMessage,
948
- subagents,
949
- skills,
950
- tools = {},
951
- processToolsInParallel = true,
952
- hooks = {},
953
- appendSystemPrompt = true,
954
- waitForInputTimeout = "48h",
955
- threadKey,
956
- sandboxOps,
957
- thread: threadInit,
958
- sandbox: sandboxInit,
959
- sandboxShutdown = "destroy",
960
- onSandboxReady,
961
- onSessionExit,
962
- virtualFs: virtualFsConfig,
963
- virtualFsOps
964
- }) {
987
+ async function createSession(config) {
988
+ const {
989
+ agentName,
990
+ maxTurns = 50,
991
+ metadata = {},
992
+ runAgent,
993
+ threadOps,
994
+ buildContextMessage,
995
+ subagents,
996
+ skills,
997
+ tools = {},
998
+ processToolsInParallel = true,
999
+ hooks = {},
1000
+ appendSystemPrompt = true,
1001
+ threadKey,
1002
+ sandboxOps,
1003
+ thread: threadInit,
1004
+ sandbox: sandboxInit,
1005
+ sandboxShutdown,
1006
+ onSandboxReady,
1007
+ onSessionExit,
1008
+ virtualFs: virtualFsConfig,
1009
+ virtualFsOps
1010
+ } = config;
1011
+ const wideOps = () => sandboxOps;
965
1012
  const threadMode = threadInit?.mode ?? "new";
966
1013
  let threadId;
967
1014
  let sourceThreadId;
@@ -1045,7 +1092,12 @@ async function createSession({
1045
1092
  stateManager.run();
1046
1093
  }
1047
1094
  );
1048
- const sandboxMode = sandboxInit?.mode;
1095
+ const lifecycle = resolveSessionLifecycle(
1096
+ sandboxInit,
1097
+ sandboxShutdown
1098
+ );
1099
+ const sandboxMode = lifecycle.mode;
1100
+ const resolvedShutdown = lifecycle.shutdown;
1049
1101
  let sandboxId;
1050
1102
  let sandboxOwned = false;
1051
1103
  let baseSnapshot;
@@ -1068,8 +1120,8 @@ async function createSession({
1068
1120
  });
1069
1121
  }
1070
1122
  sandboxId = sandboxInit.sandboxId;
1071
- if (sandboxShutdown === "pause-until-parent-close") {
1072
- await sandboxOps.resumeSandbox(sandboxId);
1123
+ if (resolvedShutdown === "pause-until-parent-close") {
1124
+ await wideOps().resumeSandbox(sandboxId);
1073
1125
  }
1074
1126
  sandboxOwned = true;
1075
1127
  } else if (sandboxMode === "fork") {
@@ -1080,7 +1132,7 @@ async function createSession({
1080
1132
  });
1081
1133
  }
1082
1134
  const forkInit = sandboxInit;
1083
- sandboxId = await sandboxOps.forkSandbox(
1135
+ sandboxId = await wideOps().forkSandbox(
1084
1136
  forkInit.sandboxId,
1085
1137
  forkInit.options
1086
1138
  );
@@ -1093,7 +1145,7 @@ async function createSession({
1093
1145
  });
1094
1146
  }
1095
1147
  const restoreInit = sandboxInit;
1096
- sandboxId = await sandboxOps.restoreSandbox(
1148
+ sandboxId = await wideOps().restoreSandbox(
1097
1149
  restoreInit.snapshot,
1098
1150
  restoreInit.options
1099
1151
  );
@@ -1109,8 +1161,8 @@ async function createSession({
1109
1161
  freshlyCreated = true;
1110
1162
  }
1111
1163
  }
1112
- if (sandboxId && sandboxOwned && freshlyCreated && sandboxShutdown === "snapshot" && sandboxOps) {
1113
- baseSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
1164
+ if (sandboxId && sandboxOwned && freshlyCreated && resolvedShutdown === "snapshot" && sandboxOps) {
1165
+ baseSnapshot = await wideOps().snapshotSandbox(sandboxId);
1114
1166
  }
1115
1167
  if (sandboxId && sandboxOwned && onSandboxReady) {
1116
1168
  onSandboxReady({
@@ -1134,12 +1186,23 @@ async function createSession({
1134
1186
  path,
1135
1187
  size: content.length,
1136
1188
  mtime: (/* @__PURE__ */ new Date()).toISOString(),
1137
- metadata: {}
1189
+ metadata: {},
1190
+ // Carry the content directly on the entry so any handler that
1191
+ // constructs a VirtualFileSystem from `fileTree` can read it
1192
+ // without needing to also wire up `inlineFiles` from state.
1193
+ inlineContent: content
1138
1194
  }))
1139
1195
  ] : result.fileTree;
1140
1196
  stateManager.mergeUpdate({
1141
1197
  fileTree,
1142
1198
  virtualFsCtx: virtualFsConfig.ctx,
1199
+ // `inlineFiles` is still the source of truth at read time:
1200
+ // VirtualFileSystem checks the inlineFiles map first and only
1201
+ // falls through to entry.inlineContent. Embedding the content on
1202
+ // the entry is the migration target so that handlers building a
1203
+ // VirtualFileSystem from `fileTree` alone (without forwarding
1204
+ // `inlineFiles` from state) can read skill resources. Until a
1205
+ // follow-up drops `inlineFiles`, both fields are populated.
1143
1206
  ...skillFiles && { inlineFiles: skillFiles }
1144
1207
  });
1145
1208
  }
@@ -1265,18 +1328,6 @@ async function createSession({
1265
1328
  continue;
1266
1329
  }
1267
1330
  assistantId = void 0;
1268
- if (stateManager.getStatus() === "WAITING_FOR_INPUT") {
1269
- const conditionMet = await condition(
1270
- () => stateManager.getStatus() === "RUNNING",
1271
- waitForInputTimeout
1272
- );
1273
- if (!conditionMet) {
1274
- stateManager.cancel();
1275
- exitReason = "cancelled";
1276
- await condition(() => false, "2s");
1277
- break;
1278
- }
1279
- }
1280
1331
  }
1281
1332
  if (stateManager.getTurns() >= maxTurns && stateManager.isRunning()) {
1282
1333
  exitReason = "max_turns";
@@ -1312,19 +1363,19 @@ async function createSession({
1312
1363
  }
1313
1364
  await callSessionEnd(exitReason, stateManager.getTurns());
1314
1365
  if (sandboxOwned && sandboxId && sandboxOps) {
1315
- switch (sandboxShutdown) {
1366
+ switch (resolvedShutdown) {
1316
1367
  case "destroy":
1317
1368
  await sandboxOps.destroySandbox(sandboxId);
1318
1369
  break;
1319
1370
  case "pause":
1320
1371
  case "pause-until-parent-close":
1321
- await sandboxOps.pauseSandbox(sandboxId);
1372
+ await wideOps().pauseSandbox(sandboxId);
1322
1373
  break;
1323
1374
  case "keep":
1324
1375
  case "keep-until-parent-close":
1325
1376
  break;
1326
1377
  case "snapshot":
1327
- exitSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
1378
+ exitSnapshot = await wideOps().snapshotSandbox(sandboxId);
1328
1379
  await sandboxOps.destroySandbox(sandboxId);
1329
1380
  break;
1330
1381
  }
@@ -1350,7 +1401,8 @@ async function createSession({
1350
1401
  onSessionExit({
1351
1402
  ...sandboxId && { sandboxId },
1352
1403
  ...exitSnapshot && { snapshot: exitSnapshot },
1353
- threadId
1404
+ threadId,
1405
+ usage: stateManager.getTotalUsage()
1354
1406
  });
1355
1407
  }
1356
1408
  return {
@@ -1638,9 +1690,24 @@ function withAutoAppend(threadHandler, handler) {
1638
1690
  };
1639
1691
  };
1640
1692
  }
1693
+ var SandboxNotSupportedError = class extends ApplicationFailure$1 {
1694
+ constructor(operation) {
1695
+ super(
1696
+ `Sandbox does not support: ${operation}`,
1697
+ "SandboxNotSupportedError",
1698
+ true
1699
+ );
1700
+ }
1701
+ };
1702
+ var SandboxNotFoundError = class extends ApplicationFailure$1 {
1703
+ constructor(sandboxId) {
1704
+ super(`Sandbox not found: ${sandboxId}`, "SandboxNotFoundError", true);
1705
+ }
1706
+ };
1641
1707
 
1642
1708
  // src/lib/tool-router/with-sandbox.ts
1643
- function withSandbox(manager, handler) {
1709
+ function withSandbox(manager, handler, options) {
1710
+ const translateSandboxNotFound = options?.translateSandboxNotFound ?? false;
1644
1711
  return async (args, context) => {
1645
1712
  if (!context.sandboxId) {
1646
1713
  return {
@@ -1648,7 +1715,18 @@ function withSandbox(manager, handler) {
1648
1715
  data: null
1649
1716
  };
1650
1717
  }
1651
- const sandbox = await manager.getSandbox(context.sandboxId);
1718
+ let sandbox;
1719
+ try {
1720
+ sandbox = await manager.getSandbox(context.sandboxId);
1721
+ } catch (err) {
1722
+ if (translateSandboxNotFound && err instanceof SandboxNotFoundError) {
1723
+ return {
1724
+ toolResponse: options?.sandboxNotFoundToolResponse ?? `Error: the execution environment for the ${context.toolName} tool is no longer available, so this tool call could not be completed.`,
1725
+ data: null
1726
+ };
1727
+ }
1728
+ throw err;
1729
+ }
1652
1730
  return handler(args, { ...context, sandbox, sandboxId: context.sandboxId });
1653
1731
  };
1654
1732
  }
@@ -1680,6 +1758,7 @@ function defineSubagentWorkflow(config, fn) {
1680
1758
  let capturedSnapshot;
1681
1759
  let capturedBaseSnapshot;
1682
1760
  let capturedThreadId;
1761
+ let capturedUsage;
1683
1762
  const sessionInput = {
1684
1763
  agentName: config.name,
1685
1764
  sandboxShutdown: effectiveShutdown,
@@ -1696,10 +1775,17 @@ function defineSubagentWorkflow(config, fn) {
1696
1775
  });
1697
1776
  }
1698
1777
  },
1699
- onSessionExit: ({ sandboxId, snapshot, threadId }) => {
1778
+ onSessionExit: ({ sandboxId, snapshot, threadId, usage }) => {
1700
1779
  capturedSandboxId = sandboxId;
1701
1780
  capturedSnapshot = snapshot;
1702
1781
  capturedThreadId = threadId;
1782
+ capturedUsage = {
1783
+ inputTokens: usage.totalInputTokens,
1784
+ outputTokens: usage.totalOutputTokens,
1785
+ cachedWriteTokens: usage.totalCachedWriteTokens,
1786
+ cachedReadTokens: usage.totalCachedReadTokens,
1787
+ reasonTokens: usage.totalReasonTokens
1788
+ };
1703
1789
  }
1704
1790
  };
1705
1791
  const result = await fn(prompt, sessionInput, context ?? {});
@@ -1710,7 +1796,8 @@ function defineSubagentWorkflow(config, fn) {
1710
1796
  ...capturedSnapshot !== void 0 && { snapshot: capturedSnapshot },
1711
1797
  ...capturedBaseSnapshot !== void 0 && {
1712
1798
  baseSnapshot: capturedBaseSnapshot
1713
- }
1799
+ },
1800
+ ...capturedUsage !== void 0 && { usage: capturedUsage }
1714
1801
  };
1715
1802
  };
1716
1803
  Object.defineProperty(workflow, "name", { value: config.name });
@@ -1794,20 +1881,6 @@ function proxyRunAgent(scope, options) {
1794
1881
  );
1795
1882
  return acts[name];
1796
1883
  }
1797
- var SandboxNotSupportedError = class extends ApplicationFailure$1 {
1798
- constructor(operation) {
1799
- super(
1800
- `Sandbox does not support: ${operation}`,
1801
- "SandboxNotSupportedError",
1802
- true
1803
- );
1804
- }
1805
- };
1806
- var SandboxNotFoundError = class extends ApplicationFailure$1 {
1807
- constructor(sandboxId) {
1808
- super(`Sandbox not found: ${sandboxId}`, "SandboxNotFoundError", true);
1809
- }
1810
- };
1811
1884
 
1812
1885
  // src/lib/virtual-fs/mutations.ts
1813
1886
  function applyVirtualTreeMutations(stateManager, mutations) {
@@ -2650,6 +2723,9 @@ function getActivityContext() {
2650
2723
  }
2651
2724
  async function queryParentWorkflowState(client) {
2652
2725
  const { workflowExecution } = Context.current().info;
2726
+ if (!workflowExecution) {
2727
+ throw new Error("No workflow execution found");
2728
+ }
2653
2729
  const handle = client.getHandle(
2654
2730
  workflowExecution.workflowId,
2655
2731
  workflowExecution.runId
@@ -2673,13 +2749,56 @@ function withParentWorkflowState(client, handler) {
2673
2749
  }
2674
2750
 
2675
2751
  // src/lib/sandbox/manager.ts
2752
+ var CAP_METHOD_TO_CAPABILITY = [
2753
+ { method: "pause", capability: "pause" },
2754
+ { method: "resume", capability: "resume" },
2755
+ { method: "snapshot", capability: "snapshot" },
2756
+ { method: "deleteSnapshot", capability: "snapshot" },
2757
+ { method: "restore", capability: "restore" },
2758
+ { method: "fork", capability: "fork" }
2759
+ ];
2676
2760
  var SandboxManager = class {
2677
2761
  constructor(provider, options) {
2678
2762
  this.provider = provider;
2679
2763
  this.hooks = options?.hooks ?? {};
2764
+ this.assertCapabilityRuntimeConsistency();
2680
2765
  }
2681
2766
  provider;
2682
2767
  hooks;
2768
+ /**
2769
+ * Verifies that the provider's runtime `supportedCapabilities` set is
2770
+ * consistent with the gated methods physically present on the provider.
2771
+ *
2772
+ * Belt-and-suspenders complement to the type-level
2773
+ * `ReadonlySet<TCaps & SandboxCapability>` constraint: TypeScript can
2774
+ * prevent the runtime set from containing capabilities not declared in
2775
+ * `TCaps`, but it cannot detect a provider that **declares** a cap in
2776
+ * `TCaps` and forgets to include it in the runtime set (or that ships
2777
+ * a method without listing its cap). Both shapes silently break
2778
+ * activity registration, so we trip a loud failure at construction
2779
+ * time instead.
2780
+ *
2781
+ * Adapters that derive both surfaces from a single `as const`
2782
+ * capability array (the recommended pattern) pass this check by
2783
+ * construction.
2784
+ */
2785
+ assertCapabilityRuntimeConsistency() {
2786
+ const supported = this.provider.supportedCapabilities;
2787
+ for (const { method, capability } of CAP_METHOD_TO_CAPABILITY) {
2788
+ const hasMethod = typeof this.provider[method] === "function";
2789
+ const declaresCap = supported.has(capability);
2790
+ if (hasMethod && !declaresCap) {
2791
+ throw new Error(
2792
+ `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.`
2793
+ );
2794
+ }
2795
+ if (declaresCap && !hasMethod) {
2796
+ throw new Error(
2797
+ `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.`
2798
+ );
2799
+ }
2800
+ }
2801
+ }
2683
2802
  async create(options, ctx) {
2684
2803
  let providerOptions = options;
2685
2804
  if (this.hooks.onPreCreate) {
@@ -2717,26 +2836,61 @@ var SandboxManager = class {
2717
2836
  async destroy(id) {
2718
2837
  await this.provider.destroy(id);
2719
2838
  }
2839
+ /**
2840
+ * Capability-gated lifecycle methods on the underlying provider.
2841
+ *
2842
+ * These manager methods always exist at runtime; calling one whose
2843
+ * capability is absent from the provider's `supportedCapabilities`
2844
+ * throws an error. The activities returned from
2845
+ * {@link SandboxManager.createActivities} are gated at the type level
2846
+ * via `TCaps`, which is where compile-time safety is enforced.
2847
+ */
2720
2848
  async pause(id, ttlSeconds) {
2721
- await this.provider.pause(id, ttlSeconds);
2849
+ const fn = this.providerMethod("pause");
2850
+ if (!fn) throw this.unsupported("pause");
2851
+ await fn.call(this.provider, id, ttlSeconds);
2722
2852
  }
2723
2853
  async resume(id) {
2724
- await this.provider.resume(id);
2854
+ const fn = this.providerMethod("resume");
2855
+ if (!fn) throw this.unsupported("resume");
2856
+ await fn.call(this.provider, id);
2725
2857
  }
2726
2858
  async snapshot(id, options) {
2727
- return this.provider.snapshot(id, options);
2859
+ const fn = this.providerMethod("snapshot");
2860
+ if (!fn) throw this.unsupported("snapshot");
2861
+ return fn.call(this.provider, id, options);
2728
2862
  }
2729
2863
  async restore(snapshot, options) {
2730
- const sandbox = await this.provider.restore(snapshot, options);
2864
+ const fn = this.providerMethod("restore");
2865
+ if (!fn) throw this.unsupported("restore");
2866
+ const sandbox = await fn.call(this.provider, snapshot, options);
2731
2867
  return sandbox.id;
2732
2868
  }
2733
2869
  async deleteSnapshot(snapshot) {
2734
- await this.provider.deleteSnapshot(snapshot);
2870
+ const fn = this.providerMethod("deleteSnapshot");
2871
+ if (!fn) throw this.unsupported("deleteSnapshot");
2872
+ await fn.call(this.provider, snapshot);
2735
2873
  }
2736
2874
  async fork(sandboxId, options) {
2737
- const sandbox = await this.provider.fork(sandboxId, options);
2875
+ const fn = this.providerMethod("fork");
2876
+ if (!fn) throw this.unsupported("fork");
2877
+ const sandbox = await fn.call(this.provider, sandboxId, options);
2738
2878
  return sandbox.id;
2739
2879
  }
2880
+ providerMethod(name) {
2881
+ const value = this.provider[name];
2882
+ return typeof value === "function" ? value : void 0;
2883
+ }
2884
+ /**
2885
+ * Constructs the structured error thrown when an unsupported lifecycle
2886
+ * method is invoked through the manager. Uses the public
2887
+ * {@link SandboxNotSupportedError} symbol so consumers that catch on
2888
+ * `instanceof SandboxNotSupportedError` (the documented compatibility
2889
+ * path) keep matching after the refactor.
2890
+ */
2891
+ unsupported(name) {
2892
+ return new SandboxNotSupportedError(name);
2893
+ }
2740
2894
  /**
2741
2895
  * Returns Temporal activity functions with prefixed names.
2742
2896
  *
@@ -2744,6 +2898,11 @@ var SandboxManager = class {
2744
2898
  * to pass the workflow/scope name. Use the matching `proxy*SandboxOps()`
2745
2899
  * helper from the adapter's `/workflow` entrypoint on the workflow side.
2746
2900
  *
2901
+ * Activities are only registered for capabilities the provider declares
2902
+ * via {@link SandboxProvider.supportedCapabilities}: methods omitted
2903
+ * from the cap set are not wrapped, and the returned object's type
2904
+ * omits the corresponding keys.
2905
+ *
2747
2906
  * @param scope - Workflow name (appended to the provider id)
2748
2907
  *
2749
2908
  * @example
@@ -2754,40 +2913,55 @@ var SandboxManager = class {
2754
2913
  *
2755
2914
  * const dmgr = new SandboxManager(new DaytonaSandboxProvider(config));
2756
2915
  * dmgr.createActivities("CodingAgent");
2757
- * // registers: daytonaCodingAgentCreateSandbox,
2916
+ * // registers: daytonaCodingAgentCreateSandbox, daytonaCodingAgentDestroySandbox
2917
+ * // (snapshot/restore/fork/pause/resume omitted — Daytona doesn't declare them)
2758
2918
  * ```
2759
2919
  */
2760
2920
  createActivities(scope) {
2761
2921
  const prefix = `${this.provider.id}${scope.charAt(0).toUpperCase()}${scope.slice(1)}`;
2922
+ const cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
2923
+ const supported = this.provider.supportedCapabilities;
2762
2924
  const ops = {
2763
2925
  createSandbox: async (options, ctx) => {
2764
2926
  return this.create(options, ctx);
2765
2927
  },
2766
2928
  destroySandbox: async (sandboxId) => {
2767
2929
  await this.destroy(sandboxId);
2768
- },
2769
- pauseSandbox: async (sandboxId, ttlSeconds) => {
2930
+ }
2931
+ };
2932
+ if (supported.has("pause")) {
2933
+ ops.pauseSandbox = async (sandboxId, ttlSeconds) => {
2770
2934
  await this.pause(sandboxId, ttlSeconds);
2771
- },
2772
- resumeSandbox: async (sandboxId) => {
2935
+ };
2936
+ }
2937
+ if (supported.has("resume")) {
2938
+ ops.resumeSandbox = async (sandboxId) => {
2773
2939
  await this.resume(sandboxId);
2774
- },
2775
- snapshotSandbox: async (sandboxId, options) => {
2940
+ };
2941
+ }
2942
+ if (supported.has("snapshot")) {
2943
+ ops.snapshotSandbox = async (sandboxId, options) => {
2776
2944
  return this.snapshot(sandboxId, options);
2777
- },
2778
- restoreSandbox: async (snapshot, options) => {
2779
- return this.restore(snapshot, options);
2780
- },
2781
- deleteSandboxSnapshot: async (snapshot) => {
2945
+ };
2946
+ ops.deleteSandboxSnapshot = async (snapshot) => {
2782
2947
  await this.deleteSnapshot(snapshot);
2783
- },
2784
- forkSandbox: async (sandboxId, options) => {
2948
+ };
2949
+ }
2950
+ if (supported.has("restore")) {
2951
+ ops.restoreSandbox = async (snapshot, options) => {
2952
+ return this.restore(snapshot, options);
2953
+ };
2954
+ }
2955
+ if (supported.has("fork")) {
2956
+ ops.forkSandbox = async (sandboxId, options) => {
2785
2957
  return this.fork(sandboxId, options);
2786
- }
2787
- };
2788
- const cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
2958
+ };
2959
+ }
2960
+ const entries = Object.entries(ops).filter(
2961
+ ([, v]) => typeof v === "function"
2962
+ );
2789
2963
  return Object.fromEntries(
2790
- Object.entries(ops).map(([k, v]) => [`${prefix}${cap(k)}`, v])
2964
+ entries.map(([k, v]) => [`${prefix}${cap(k)}`, v])
2791
2965
  );
2792
2966
  }
2793
2967
  };
@@ -2920,7 +3094,7 @@ var VirtualFileSystem = class {
2920
3094
  if (inline !== void 0) return inline;
2921
3095
  const entry = this.entries.get(norm);
2922
3096
  if (!entry) throw new Error(`ENOENT: no such file: ${path}`);
2923
- return this.resolver.readFile(entry.id, this.ctx, entry.metadata);
3097
+ return this.readEntryContent(entry);
2924
3098
  }
2925
3099
  async readFileBuffer(path) {
2926
3100
  const norm = normalisePath(path, this.workspaceBase);
@@ -2928,8 +3102,23 @@ var VirtualFileSystem = class {
2928
3102
  if (inline !== void 0) return new TextEncoder().encode(inline);
2929
3103
  const entry = this.entries.get(norm);
2930
3104
  if (!entry) throw new Error(`ENOENT: no such file: ${path}`);
3105
+ if (entry.inlineContent !== void 0) {
3106
+ return new TextEncoder().encode(entry.inlineContent);
3107
+ }
2931
3108
  return this.resolver.readFileBuffer(entry.id, this.ctx, entry.metadata);
2932
3109
  }
3110
+ /**
3111
+ * Resolve the string content for an entry, preferring inline content
3112
+ * carried on the entry itself before consulting the resolver. Used by
3113
+ * `readFile`, `appendFile`, and `cp` so all read paths agree on the
3114
+ * lookup precedence.
3115
+ */
3116
+ readEntryContent(entry) {
3117
+ if (entry.inlineContent !== void 0) {
3118
+ return Promise.resolve(entry.inlineContent);
3119
+ }
3120
+ return this.resolver.readFile(entry.id, this.ctx, entry.metadata);
3121
+ }
2933
3122
  // --------------------------------------------------------------------------
2934
3123
  // Metadata operations — pure, resolved from the tree
2935
3124
  // --------------------------------------------------------------------------
@@ -3001,6 +3190,11 @@ var VirtualFileSystem = class {
3001
3190
  const norm = normalisePath(path, this.workspaceBase);
3002
3191
  const existing = this.entries.get(norm);
3003
3192
  if (existing) {
3193
+ if (existing.inlineContent !== void 0) {
3194
+ throw new Error(
3195
+ `EROFS: cannot write to inline (read-only) entry: ${path}`
3196
+ );
3197
+ }
3004
3198
  await this.resolver.writeFile(
3005
3199
  existing.id,
3006
3200
  content,
@@ -3029,11 +3223,12 @@ var VirtualFileSystem = class {
3029
3223
  if (!existing) {
3030
3224
  return this.writeFile(path, content);
3031
3225
  }
3032
- const current = await this.resolver.readFile(
3033
- existing.id,
3034
- this.ctx,
3035
- existing.metadata
3036
- );
3226
+ if (existing.inlineContent !== void 0) {
3227
+ throw new Error(
3228
+ `EROFS: cannot append to inline (read-only) entry: ${path}`
3229
+ );
3230
+ }
3231
+ const current = await this.readEntryContent(existing);
3037
3232
  const appended = typeof content === "string" ? current + content : current + new TextDecoder().decode(content);
3038
3233
  await this.resolver.writeFile(
3039
3234
  existing.id,
@@ -3068,6 +3263,11 @@ var VirtualFileSystem = class {
3068
3263
  const norm = normalisePath(path, this.workspaceBase);
3069
3264
  const entry = this.entries.get(norm);
3070
3265
  if (entry) {
3266
+ if (entry.inlineContent !== void 0) {
3267
+ throw new Error(
3268
+ `EROFS: cannot remove inline (read-only) entry: ${path}`
3269
+ );
3270
+ }
3071
3271
  await this.resolver.deleteFile(entry.id, this.ctx, entry.metadata);
3072
3272
  this.entries.delete(norm);
3073
3273
  this.mutations.push({ type: "remove", path: norm });
@@ -3080,6 +3280,11 @@ var VirtualFileSystem = class {
3080
3280
  const prefix = norm === "/" ? "/" : norm + "/";
3081
3281
  for (const [p, e] of this.entries) {
3082
3282
  if (p.startsWith(prefix)) {
3283
+ if (e.inlineContent !== void 0) {
3284
+ throw new Error(
3285
+ `EROFS: cannot remove inline (read-only) entry: ${p}`
3286
+ );
3287
+ }
3083
3288
  await this.resolver.deleteFile(e.id, this.ctx, e.metadata);
3084
3289
  this.entries.delete(p);
3085
3290
  this.mutations.push({ type: "remove", path: p });
@@ -3100,11 +3305,7 @@ var VirtualFileSystem = class {
3100
3305
  const normDest = normalisePath(dest, this.workspaceBase);
3101
3306
  const entry = this.entries.get(normSrc);
3102
3307
  if (entry) {
3103
- const content = await this.resolver.readFile(
3104
- entry.id,
3105
- this.ctx,
3106
- entry.metadata
3107
- );
3308
+ const content = await this.readEntryContent(entry);
3108
3309
  await this.writeFile(normDest, content);
3109
3310
  return;
3110
3311
  }
@@ -3118,11 +3319,7 @@ var VirtualFileSystem = class {
3118
3319
  for (const [p, e] of this.entries) {
3119
3320
  if (p.startsWith(prefix)) {
3120
3321
  const relative = p.slice(normSrc.length);
3121
- const content = await this.resolver.readFile(
3122
- e.id,
3123
- this.ctx,
3124
- e.metadata
3125
- );
3322
+ const content = await this.readEntryContent(e);
3126
3323
  await this.writeFile(normDest + relative, content);
3127
3324
  }
3128
3325
  }