zeitlich 0.2.36 → 0.2.38

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 (204) hide show
  1. package/README.md +146 -92
  2. package/dist/{activities-BVI2lTwr.d.ts → activities-BKhMtKDd.d.ts} +4 -2
  3. package/dist/{activities-hd4aNnZE.d.cts → activities-CDcwkRZs.d.cts} +4 -2
  4. package/dist/adapters/sandbox/bedrock/index.cjs +17 -14
  5. package/dist/adapters/sandbox/bedrock/index.cjs.map +1 -1
  6. package/dist/adapters/sandbox/bedrock/index.d.cts +7 -6
  7. package/dist/adapters/sandbox/bedrock/index.d.ts +7 -6
  8. package/dist/adapters/sandbox/bedrock/index.js +17 -14
  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 +11 -3
  17. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  18. package/dist/adapters/sandbox/daytona/index.d.cts +5 -4
  19. package/dist/adapters/sandbox/daytona/index.d.ts +5 -4
  20. package/dist/adapters/sandbox/daytona/index.js +11 -3
  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 +73 -12
  29. package/dist/adapters/sandbox/e2b/index.cjs.map +1 -1
  30. package/dist/adapters/sandbox/e2b/index.d.cts +26 -4
  31. package/dist/adapters/sandbox/e2b/index.d.ts +26 -4
  32. package/dist/adapters/sandbox/e2b/index.js +73 -12
  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 +8 -3
  41. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
  42. package/dist/adapters/sandbox/inmemory/index.d.cts +5 -4
  43. package/dist/adapters/sandbox/inmemory/index.d.ts +5 -4
  44. package/dist/adapters/sandbox/inmemory/index.js +8 -3
  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 +94 -39
  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 +94 -39
  57. package/dist/adapters/thread/anthropic/index.js.map +1 -1
  58. package/dist/adapters/thread/anthropic/workflow.cjs +7 -2
  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 +7 -2
  63. package/dist/adapters/thread/anthropic/workflow.js.map +1 -1
  64. package/dist/adapters/thread/google-genai/index.cjs +77 -28
  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 +77 -28
  69. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  70. package/dist/adapters/thread/google-genai/workflow.cjs +7 -2
  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 +7 -2
  75. package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
  76. package/dist/adapters/thread/langchain/index.cjs +57 -10
  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 +57 -10
  81. package/dist/adapters/thread/langchain/index.js.map +1 -1
  82. package/dist/adapters/thread/langchain/workflow.cjs +7 -2
  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 +7 -2
  87. package/dist/adapters/thread/langchain/workflow.js.map +1 -1
  88. package/dist/index.cjs +322 -146
  89. package/dist/index.cjs.map +1 -1
  90. package/dist/index.d.cts +20 -14
  91. package/dist/index.d.ts +20 -14
  92. package/dist/index.js +323 -147
  93. package/dist/index.js.map +1 -1
  94. package/dist/{proxy-BjdFGPTm.d.ts → proxy-CUlKSvZS.d.ts} +1 -1
  95. package/dist/{proxy-7RnVaPdJ.d.cts → proxy-D_3x7RN4.d.cts} +1 -1
  96. package/dist/{thread-manager-CbpiGq1L.d.ts → thread-manager-CVu7o2cs.d.ts} +4 -2
  97. package/dist/{thread-manager-DzXm9eeI.d.cts → thread-manager-HSwyh28L.d.cts} +4 -2
  98. package/dist/{thread-manager-BBzNgQWH.d.cts → thread-manager-c1gPopAG.d.ts} +4 -2
  99. package/dist/{thread-manager-DjN5JYul.d.ts → thread-manager-wGi-LqIP.d.cts} +4 -2
  100. package/dist/{types-Mc_4BCfT.d.cts → types-BH_IRryz.d.ts} +10 -1
  101. package/dist/{types-yiXmqedU.d.ts → types-BaOw4hKI.d.cts} +10 -1
  102. package/dist/{types-DQ1l_gXL.d.cts → types-C06FwR96.d.cts} +121 -17
  103. package/dist/{types-wiGLvxWf.d.ts → types-DAsQ21Rt.d.ts} +1 -1
  104. package/dist/{types-CADc5V_P.d.ts → types-DNr31FzL.d.ts} +121 -17
  105. package/dist/{types-CBH54cwr.d.cts → types-lm8tMNJQ.d.cts} +1 -1
  106. package/dist/{types-DxCpFNv_.d.cts → types-yx0LzPGn.d.cts} +44 -5
  107. package/dist/{types-DxCpFNv_.d.ts → types-yx0LzPGn.d.ts} +44 -5
  108. package/dist/{workflow-DhtWRovz.d.cts → workflow-CSCkpwAL.d.ts} +2 -2
  109. package/dist/{workflow-P2pTSfKu.d.ts → workflow-DuvMZ8Vm.d.cts} +2 -2
  110. package/dist/workflow.cjs +274 -130
  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 +275 -131
  115. package/dist/workflow.js.map +1 -1
  116. package/package.json +2 -2
  117. package/src/adapters/sandbox/bedrock/filesystem.ts +6 -12
  118. package/src/adapters/sandbox/bedrock/index.ts +22 -11
  119. package/src/adapters/sandbox/bedrock/proxy.ts +2 -0
  120. package/src/adapters/sandbox/daytona/index.ts +18 -3
  121. package/src/adapters/sandbox/daytona/proxy.ts +2 -0
  122. package/src/adapters/sandbox/e2b/filesystem.ts +5 -4
  123. package/src/adapters/sandbox/e2b/index.ts +87 -14
  124. package/src/adapters/sandbox/e2b/proxy.ts +2 -0
  125. package/src/adapters/sandbox/e2b/types.ts +16 -0
  126. package/src/adapters/sandbox/inmemory/index.ts +17 -3
  127. package/src/adapters/sandbox/inmemory/proxy.ts +2 -0
  128. package/src/adapters/thread/anthropic/activities.ts +58 -26
  129. package/src/adapters/thread/anthropic/model-invoker.ts +18 -7
  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 +63 -46
  133. package/src/adapters/thread/google-genai/activities.ts +20 -2
  134. package/src/adapters/thread/google-genai/model-invoker.ts +27 -7
  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 +57 -33
  138. package/src/adapters/thread/langchain/activities.ts +55 -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 +5 -4
  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 +23 -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 +18 -3
  149. package/src/lib/model/proxy.ts +2 -2
  150. package/src/lib/model/types.ts +10 -0
  151. package/src/lib/observability/hooks.ts +4 -5
  152. package/src/lib/observability/index.ts +1 -4
  153. package/src/lib/sandbox/manager.ts +45 -20
  154. package/src/lib/sandbox/node-fs.ts +3 -6
  155. package/src/lib/sandbox/sandbox.test.ts +36 -3
  156. package/src/lib/sandbox/tree.integration.test.ts +10 -3
  157. package/src/lib/sandbox/types.ts +60 -6
  158. package/src/lib/session/session-edge-cases.integration.test.ts +316 -14
  159. package/src/lib/session/session.integration.test.ts +161 -1
  160. package/src/lib/session/session.ts +106 -21
  161. package/src/lib/session/types.ts +25 -5
  162. package/src/lib/skills/fs-provider.ts +12 -8
  163. package/src/lib/skills/handler.ts +1 -1
  164. package/src/lib/skills/parse.ts +3 -1
  165. package/src/lib/skills/register.ts +1 -3
  166. package/src/lib/skills/skills.integration.test.ts +25 -15
  167. package/src/lib/state/manager.integration.test.ts +12 -2
  168. package/src/lib/subagent/define.ts +1 -1
  169. package/src/lib/subagent/handler.ts +186 -71
  170. package/src/lib/subagent/index.ts +1 -5
  171. package/src/lib/subagent/register.ts +3 -2
  172. package/src/lib/subagent/signals.ts +1 -10
  173. package/src/lib/subagent/subagent.integration.test.ts +526 -248
  174. package/src/lib/subagent/tool.ts +4 -3
  175. package/src/lib/subagent/types.ts +50 -20
  176. package/src/lib/subagent/workflow.ts +9 -49
  177. package/src/lib/thread/id.test.ts +1 -1
  178. package/src/lib/thread/id.ts +1 -2
  179. package/src/lib/thread/manager.ts +18 -0
  180. package/src/lib/thread/proxy.ts +4 -4
  181. package/src/lib/thread/types.ts +20 -3
  182. package/src/lib/tool-router/index.ts +3 -5
  183. package/src/lib/tool-router/router-edge-cases.integration.test.ts +93 -1
  184. package/src/lib/tool-router/router.integration.test.ts +12 -0
  185. package/src/lib/tool-router/router.ts +90 -16
  186. package/src/lib/tool-router/types.ts +45 -4
  187. package/src/lib/tool-router/with-sandbox.ts +19 -5
  188. package/src/lib/virtual-fs/filesystem.ts +1 -1
  189. package/src/lib/virtual-fs/index.ts +5 -1
  190. package/src/lib/virtual-fs/mutations.ts +2 -4
  191. package/src/lib/virtual-fs/queries.ts +9 -5
  192. package/src/lib/virtual-fs/types.ts +4 -1
  193. package/src/lib/virtual-fs/virtual-fs.test.ts +9 -11
  194. package/src/lib/workflow.test.ts +7 -4
  195. package/src/lib/workflow.ts +1 -5
  196. package/src/tools/ask-user-question/tool.ts +1 -3
  197. package/src/tools/glob/handler.ts +1 -4
  198. package/src/tools/task-get/handler.ts +4 -5
  199. package/src/tools/task-list/handler.ts +1 -4
  200. package/src/tools/task-update/handler.ts +4 -5
  201. package/src/workflow.ts +22 -7
  202. package/tsup.config.ts +9 -6
  203. package/src/lib/.env +0 -1
  204. package/src/tools/bash/.env +0 -1
package/dist/index.cjs CHANGED
@@ -117,7 +117,7 @@ function createToolRouter(options) {
117
117
  });
118
118
  }
119
119
  }
120
- async function processToolCall(toolCall, turn, sandboxId) {
120
+ async function processToolCall(toolCall, turn, sandboxId, onRewindRequested) {
121
121
  const startTime = Date.now();
122
122
  const tool = toolMap.get(toolCall.name);
123
123
  const preResult = await runPreHooks(toolCall, tool, turn);
@@ -132,7 +132,7 @@ function createToolRouter(options) {
132
132
  reason: "Skipped by PreToolUse hook"
133
133
  })
134
134
  });
135
- return null;
135
+ return { kind: "skipped" };
136
136
  }
137
137
  const effectiveArgs = preResult.args;
138
138
  workflow.log.debug("tool call dispatched", {
@@ -144,6 +144,7 @@ function createToolRouter(options) {
144
144
  let content;
145
145
  let resultAppended = false;
146
146
  let metadata;
147
+ let rewindRequested = false;
147
148
  try {
148
149
  if (tool) {
149
150
  const routerContext = {
@@ -161,11 +162,15 @@ function createToolRouter(options) {
161
162
  content = response.toolResponse;
162
163
  resultAppended = response.resultAppended === true;
163
164
  metadata = response.metadata;
165
+ rewindRequested = response.rewind === true;
164
166
  } else {
165
167
  result = { error: `Unknown tool: ${toolCall.name}` };
166
168
  content = JSON.stringify(result, null, 2);
167
169
  }
168
170
  } catch (error) {
171
+ if (workflow.isCancellation(error)) {
172
+ throw error;
173
+ }
169
174
  workflow.log.warn("tool call failed", {
170
175
  toolName: toolCall.name,
171
176
  toolCallId: toolCall.id,
@@ -183,6 +188,15 @@ function createToolRouter(options) {
183
188
  result = recovery.result;
184
189
  content = recovery.content;
185
190
  }
191
+ if (rewindRequested) {
192
+ const signal = {
193
+ toolCallId: toolCall.id,
194
+ toolName: toolCall.name
195
+ };
196
+ workflow.log.info("tool requested rewind", { ...signal });
197
+ onRewindRequested?.(signal);
198
+ return { kind: "rewind", signal };
199
+ }
186
200
  if (!resultAppended) {
187
201
  const config = {
188
202
  threadId: options.threadId,
@@ -219,7 +233,7 @@ function createToolRouter(options) {
219
233
  turn,
220
234
  durationMs
221
235
  );
222
- return toolResult;
236
+ return { kind: "result", value: toolResult };
223
237
  }
224
238
  return {
225
239
  hasTools() {
@@ -254,27 +268,59 @@ function createToolRouter(options) {
254
268
  }));
255
269
  },
256
270
  async processToolCalls(toolCalls, context) {
271
+ const attachRewind = (arr, rewind) => {
272
+ if (rewind) {
273
+ arr.rewind = rewind;
274
+ }
275
+ return arr;
276
+ };
257
277
  if (toolCalls.length === 0) {
258
- return [];
278
+ return attachRewind([], void 0);
259
279
  }
260
280
  const turn = context?.turn ?? 0;
261
281
  const sandboxId = context?.sandboxId;
282
+ let rewindSignal;
262
283
  if (options.parallel) {
263
- const results2 = await Promise.all(
264
- toolCalls.map((tc) => processToolCall(tc, turn, sandboxId))
265
- );
266
- return results2.filter(
267
- (r) => r !== null
284
+ const scope = new workflow.CancellationScope({ cancellable: true });
285
+ const onRewindRequested = (signal) => {
286
+ if (!rewindSignal) {
287
+ rewindSignal = signal;
288
+ scope.cancel();
289
+ }
290
+ };
291
+ const outcomes = await scope.run(
292
+ async () => Promise.allSettled(
293
+ toolCalls.map(
294
+ (tc) => processToolCall(tc, turn, sandboxId, onRewindRequested)
295
+ )
296
+ )
268
297
  );
298
+ const results2 = [];
299
+ for (const outcome of outcomes) {
300
+ if (outcome.status === "rejected") {
301
+ if (workflow.isCancellation(outcome.reason)) {
302
+ continue;
303
+ }
304
+ throw outcome.reason;
305
+ }
306
+ if (outcome.value.kind === "result") {
307
+ results2.push(outcome.value.value);
308
+ }
309
+ }
310
+ return attachRewind(results2, rewindSignal);
269
311
  }
270
312
  const results = [];
271
313
  for (const toolCall of toolCalls) {
272
- const result = await processToolCall(toolCall, turn, sandboxId);
273
- if (result !== null) {
274
- results.push(result);
314
+ const outcome = await processToolCall(toolCall, turn, sandboxId);
315
+ if (outcome.kind === "rewind") {
316
+ rewindSignal = outcome.signal;
317
+ break;
318
+ }
319
+ if (outcome.kind === "result") {
320
+ results.push(outcome.value);
275
321
  }
276
322
  }
277
- return results;
323
+ return attachRewind(results, rewindSignal);
278
324
  },
279
325
  async processToolCallsByName(toolCalls, toolName, handler, context) {
280
326
  const matchingCalls = toolCalls.filter((tc) => tc.name === toolName);
@@ -395,9 +441,7 @@ function createSubagentTool(subagents) {
395
441
  schema
396
442
  };
397
443
  }
398
- var childResultSignal = workflow.defineSignal("childResult");
399
444
  var childSandboxReadySignal = workflow.defineSignal("childSandboxReady");
400
- var destroySandboxSignal = workflow.defineSignal("destroySandbox");
401
445
 
402
446
  // src/lib/subagent/handler.ts
403
447
  function resolveSandboxConfig(config) {
@@ -421,25 +465,27 @@ function resolveSandboxConfig(config) {
421
465
  }
422
466
  function createSubagentHandler(subagents) {
423
467
  const { taskQueue: parentTaskQueue } = workflow.workflowInfo();
424
- const childResults = /* @__PURE__ */ new Map();
468
+ const agentSandboxOps = /* @__PURE__ */ new Map();
469
+ for (const cfg of subagents) {
470
+ if (cfg.sandbox && cfg.sandbox !== "none") {
471
+ agentSandboxOps.set(cfg.agentName, cfg.sandbox.proxy(cfg.agentName));
472
+ }
473
+ }
425
474
  const pendingDestroys = /* @__PURE__ */ new Map();
426
475
  const threadSandboxes = /* @__PURE__ */ new Map();
427
476
  const persistentSandboxes = /* @__PURE__ */ new Map();
428
477
  const persistentSandboxCreating = /* @__PURE__ */ new Set();
429
478
  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
- }
479
+ const threadSnapshots = /* @__PURE__ */ new Map();
480
+ const persistentBaseSnapshot = /* @__PURE__ */ new Map();
481
+ const persistentBaseSnapshotCreating = /* @__PURE__ */ new Set();
482
+ workflow.setHandler(childSandboxReadySignal, ({ childWorkflowId, sandboxId }) => {
483
+ const agentName = lazyCreatorAgent.get(childWorkflowId);
484
+ if (agentName && !persistentSandboxes.has(agentName)) {
485
+ persistentSandboxes.set(agentName, sandboxId);
486
+ lazyCreatorAgent.delete(childWorkflowId);
441
487
  }
442
- );
488
+ });
443
489
  const handler = async (args, context) => {
444
490
  const config = subagents.find((s) => s.agentName === args.subagent);
445
491
  if (!config) {
@@ -450,6 +496,12 @@ function createSubagentHandler(subagents) {
450
496
  const childWorkflowId = `${args.subagent}-${getShortId()}`;
451
497
  const { sandboxId: parentSandboxId } = context;
452
498
  const sandboxCfg = resolveSandboxConfig(config.sandbox);
499
+ if (sandboxCfg.source !== "none" && !agentSandboxOps.has(config.agentName)) {
500
+ throw workflow.ApplicationFailure.create({
501
+ message: `Subagent "${config.agentName}" uses a sandbox but no \`sandbox.proxy\` is configured on its SubagentConfig`,
502
+ nonRetryable: true
503
+ });
504
+ }
453
505
  if (sandboxCfg.source === "inherit" && !parentSandboxId) {
454
506
  throw new Error(
455
507
  `Subagent "${config.agentName}" is configured with sandbox: "inherit" but the parent has no sandbox`
@@ -468,12 +520,39 @@ function createSubagentHandler(subagents) {
468
520
  let sandbox;
469
521
  let sandboxShutdownOverride;
470
522
  let isLazyCreator = false;
523
+ let isSnapshotBaseCreator = false;
471
524
  if (sandboxCfg.source === "inherit" && parentSandboxId) {
472
525
  if (sandboxCfg.continuation === "fork") {
473
526
  sandbox = { mode: "fork", sandboxId: parentSandboxId };
527
+ } else if (sandboxCfg.continuation === "snapshot") {
528
+ throw new Error(
529
+ `Subagent "${config.agentName}" has sandbox source "inherit" with continuation "snapshot" \u2014 snapshot continuation is only supported for source "own"`
530
+ );
474
531
  } else {
475
532
  sandbox = { mode: "inherit", sandboxId: parentSandboxId };
476
533
  }
534
+ } else if (sandboxCfg.source === "own" && sandboxCfg.continuation === "snapshot") {
535
+ const isLazy = sandboxCfg.init === "once";
536
+ let baseSnap;
537
+ if (continuationThreadId) {
538
+ baseSnap = threadSnapshots.get(continuationThreadId)?.snapshot;
539
+ }
540
+ if (!baseSnap && isLazy) {
541
+ baseSnap = persistentBaseSnapshot.get(config.agentName);
542
+ if (!baseSnap) {
543
+ if (persistentBaseSnapshotCreating.has(config.agentName)) {
544
+ await workflow.condition(() => persistentBaseSnapshot.has(config.agentName));
545
+ baseSnap = persistentBaseSnapshot.get(config.agentName);
546
+ } else {
547
+ persistentBaseSnapshotCreating.add(config.agentName);
548
+ isSnapshotBaseCreator = true;
549
+ }
550
+ }
551
+ }
552
+ if (baseSnap) {
553
+ sandbox = { mode: "from-snapshot", snapshot: baseSnap };
554
+ }
555
+ sandboxShutdownOverride = "snapshot";
477
556
  } else if (sandboxCfg.source === "own") {
478
557
  const isLazy = sandboxCfg.init === "once";
479
558
  let baseSandboxId;
@@ -524,31 +603,8 @@ function createSubagentHandler(subagents) {
524
603
  threadMode,
525
604
  sandboxSource: sandboxCfg.source
526
605
  });
527
- const childHandle = await workflow.startChild(config.workflow, childOpts);
606
+ const childResult = await workflow.executeChild(config.workflow, childOpts);
528
607
  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
608
  workflow.log.info("subagent completed", {
553
609
  subagent: config.agentName,
554
610
  childWorkflowId,
@@ -560,19 +616,42 @@ function createSubagentHandler(subagents) {
560
616
  usage,
561
617
  threadId: childThreadId,
562
618
  sandboxId: childSandboxId,
619
+ snapshot: childSnapshot,
620
+ baseSnapshot: childBaseSnapshot,
563
621
  metadata
564
622
  } = childResult;
565
623
  if (childSandboxId) {
566
- if (sandboxCfg.source === "own" && sandboxCfg.init === "once" && !persistentSandboxes.has(config.agentName)) {
624
+ if (sandboxCfg.source === "own" && sandboxCfg.init === "once" && sandboxCfg.continuation !== "snapshot" && !persistentSandboxes.has(config.agentName)) {
567
625
  persistentSandboxes.set(config.agentName, childSandboxId);
568
- } else if (allowsContinuation && childThreadId) {
626
+ } else if (allowsContinuation && childThreadId && sandboxCfg.source === "own" && sandboxCfg.continuation !== "snapshot") {
569
627
  threadSandboxes.set(childThreadId, childSandboxId);
570
628
  }
571
629
  }
630
+ if (childSandboxId && (effectiveShutdown === "pause-until-parent-close" || effectiveShutdown === "keep-until-parent-close")) {
631
+ const key = isLazyCreator ? `persistent:${config.agentName}` : childWorkflowId;
632
+ pendingDestroys.set(key, {
633
+ agentName: config.agentName,
634
+ sandboxId: childSandboxId
635
+ });
636
+ }
637
+ if (sandboxCfg.source === "own" && sandboxCfg.continuation === "snapshot") {
638
+ if (childSnapshot && childThreadId) {
639
+ threadSnapshots.set(childThreadId, {
640
+ agentName: config.agentName,
641
+ snapshot: childSnapshot
642
+ });
643
+ }
644
+ if (isSnapshotBaseCreator && childBaseSnapshot && !persistentBaseSnapshot.has(config.agentName)) {
645
+ persistentBaseSnapshot.set(config.agentName, childBaseSnapshot);
646
+ }
647
+ }
572
648
  if (isLazyCreator) {
573
649
  persistentSandboxCreating.delete(config.agentName);
574
650
  lazyCreatorAgent.delete(childWorkflowId);
575
651
  }
652
+ if (isSnapshotBaseCreator) {
653
+ persistentBaseSnapshotCreating.delete(config.agentName);
654
+ }
576
655
  if (!toolResponse) {
577
656
  return {
578
657
  toolResponse: "Subagent workflow returned no response",
@@ -608,22 +687,60 @@ function createSubagentHandler(subagents) {
608
687
  };
609
688
  };
610
689
  const destroySubagentSandboxes = async () => {
611
- const handles = [...pendingDestroys.values()];
690
+ const entries = [...pendingDestroys.values()];
612
691
  pendingDestroys.clear();
613
692
  await Promise.all(
614
- handles.map(async (handle) => {
693
+ entries.map(async ({ agentName, sandboxId }) => {
694
+ const ops = agentSandboxOps.get(agentName);
695
+ if (!ops) {
696
+ workflow.log.warn(
697
+ "Skipping sandbox destroy \u2014 no sandbox.proxy registered for agent",
698
+ { agentName, sandboxId }
699
+ );
700
+ return;
701
+ }
702
+ try {
703
+ await ops.destroySandbox(sandboxId);
704
+ } catch (err) {
705
+ workflow.log.warn("Failed to destroy subagent sandbox", {
706
+ agentName,
707
+ sandboxId,
708
+ error: err
709
+ });
710
+ }
711
+ })
712
+ );
713
+ };
714
+ const cleanupSubagentSnapshots = async () => {
715
+ const tagged = [];
716
+ for (const entry of threadSnapshots.values()) tagged.push(entry);
717
+ for (const [agentName, snapshot] of persistentBaseSnapshot.entries()) {
718
+ tagged.push({ agentName, snapshot });
719
+ }
720
+ threadSnapshots.clear();
721
+ persistentBaseSnapshot.clear();
722
+ await Promise.all(
723
+ tagged.map(async ({ agentName, snapshot }) => {
724
+ const ops = agentSandboxOps.get(agentName);
725
+ if (!ops) {
726
+ workflow.log.warn(
727
+ "Skipping snapshot delete \u2014 no sandbox.proxy registered for agent",
728
+ { agentName }
729
+ );
730
+ return;
731
+ }
615
732
  try {
616
- await handle.signal(destroySandboxSignal);
617
- await handle.result();
733
+ await ops.deleteSandboxSnapshot(snapshot);
618
734
  } catch (err) {
619
- workflow.log.warn("Failed to signal destroySandbox to child workflow", {
735
+ workflow.log.warn("Failed to delete subagent snapshot", {
736
+ agentName,
620
737
  error: err
621
738
  });
622
739
  }
623
740
  })
624
741
  );
625
742
  };
626
- return { handler, destroySubagentSandboxes };
743
+ return { handler, destroySubagentSandboxes, cleanupSubagentSnapshots };
627
744
  }
628
745
 
629
746
  // src/lib/subagent/register.ts
@@ -637,7 +754,7 @@ function buildSubagentRegistration(subagents) {
637
754
  if (s.hooks) subagentHooksMap.set(s.agentName, s.hooks);
638
755
  }
639
756
  const resolveSubagentName = (args) => args.subagent;
640
- const { handler, destroySubagentSandboxes } = createSubagentHandler(subagents);
757
+ const { handler, destroySubagentSandboxes, cleanupSubagentSnapshots } = createSubagentHandler(subagents);
641
758
  const registration = {
642
759
  name: SUBAGENT_TOOL_NAME,
643
760
  enabled: () => getEnabled().length > 0,
@@ -661,7 +778,7 @@ function buildSubagentRegistration(subagents) {
661
778
  }
662
779
  }
663
780
  };
664
- return { registration, destroySubagentSandboxes };
781
+ return { registration, destroySubagentSandboxes, cleanupSubagentSnapshots };
665
782
  }
666
783
  var READ_SKILL_TOOL_NAME = "ReadSkill";
667
784
  function buildReadSkillDescription(skills) {
@@ -734,9 +851,7 @@ function validateSkillNames(skills) {
734
851
  const names = skills.map((s) => s.name);
735
852
  const dupes = names.filter((n, i) => names.indexOf(n) !== i);
736
853
  if (dupes.length > 0) {
737
- throw new Error(
738
- `Duplicate skill names: ${[...new Set(dupes)].join(", ")}`
739
- );
854
+ throw new Error(`Duplicate skill names: ${[...new Set(dupes)].join(", ")}`);
740
855
  }
741
856
  }
742
857
  function buildSkillRegistration(skills) {
@@ -802,15 +917,18 @@ async function createSession({
802
917
  initializeThread,
803
918
  appendSystemMessage,
804
919
  appendAgentMessage,
805
- forkThread
920
+ forkThread,
921
+ truncateThread
806
922
  } = threadOps;
807
923
  const plugins = [];
808
924
  let destroySubagentSandboxes;
925
+ let cleanupSubagentSnapshots;
809
926
  if (subagents) {
810
927
  const result = buildSubagentRegistration(subagents);
811
928
  if (result) {
812
929
  plugins.push(result.registration);
813
930
  destroySubagentSandboxes = result.destroySubagentSandboxes;
931
+ cleanupSubagentSnapshots = result.cleanupSubagentSnapshots;
814
932
  }
815
933
  }
816
934
  if (skills) {
@@ -863,6 +981,9 @@ async function createSession({
863
981
  const sandboxMode = sandboxInit?.mode;
864
982
  let sandboxId;
865
983
  let sandboxOwned = false;
984
+ let baseSnapshot;
985
+ let exitSnapshot;
986
+ let freshlyCreated = false;
866
987
  if (sandboxMode === "inherit") {
867
988
  const inheritInit = sandboxInit;
868
989
  sandboxId = inheritInit.sandboxId;
@@ -891,8 +1012,23 @@ async function createSession({
891
1012
  nonRetryable: true
892
1013
  });
893
1014
  }
1015
+ const forkInit = sandboxInit;
894
1016
  sandboxId = await sandboxOps.forkSandbox(
895
- sandboxInit.sandboxId
1017
+ forkInit.sandboxId,
1018
+ forkInit.options
1019
+ );
1020
+ sandboxOwned = true;
1021
+ } else if (sandboxMode === "from-snapshot") {
1022
+ if (!sandboxOps) {
1023
+ throw workflow.ApplicationFailure.create({
1024
+ message: "No sandboxOps provided \u2014 cannot restore sandbox",
1025
+ nonRetryable: true
1026
+ });
1027
+ }
1028
+ const restoreInit = sandboxInit;
1029
+ sandboxId = await sandboxOps.restoreSandbox(
1030
+ restoreInit.snapshot,
1031
+ restoreInit.options
896
1032
  );
897
1033
  sandboxOwned = true;
898
1034
  } else if (sandboxOps) {
@@ -903,9 +1039,13 @@ async function createSession({
903
1039
  if (result) {
904
1040
  sandboxId = result.sandboxId;
905
1041
  sandboxOwned = true;
1042
+ freshlyCreated = true;
906
1043
  }
907
1044
  }
908
- if (sandboxId && onSandboxReady) {
1045
+ if (sandboxId && sandboxOwned && freshlyCreated && sandboxShutdown === "snapshot" && sandboxOps) {
1046
+ baseSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
1047
+ }
1048
+ if (sandboxId && sandboxOwned && onSandboxReady) {
909
1049
  onSandboxReady(sandboxId);
910
1050
  }
911
1051
  if (virtualFsConfig) {
@@ -971,18 +1111,25 @@ async function createSession({
971
1111
  threadKey
972
1112
  );
973
1113
  let exitReason = "completed";
1114
+ let finalMessage = null;
974
1115
  try {
975
1116
  while (stateManager.isRunning() && !stateManager.isTerminal() && stateManager.getTurns() < maxTurns) {
976
1117
  stateManager.incrementTurns();
977
1118
  const currentTurn = stateManager.getTurns();
978
1119
  workflow.log.debug("turn started", { agentName, threadId, turn: currentTurn });
979
1120
  stateManager.setTools(toolRouter.getToolDefinitions());
980
- const { message, rawToolCalls, usage } = await runAgent({
1121
+ const {
1122
+ message,
1123
+ rawToolCalls,
1124
+ usage,
1125
+ threadLengthAtCall
1126
+ } = await runAgent({
981
1127
  threadId,
982
1128
  threadKey,
983
1129
  agentName,
984
1130
  metadata
985
1131
  });
1132
+ const preAssistantLength = threadLengthAtCall;
986
1133
  await appendAgentMessage(threadId, workflow.uuid4(), message, threadKey);
987
1134
  if (usage) {
988
1135
  stateManager.updateUsage(usage);
@@ -997,21 +1144,8 @@ async function createSession({
997
1144
  if (!toolRouter.hasTools() || rawToolCalls.length === 0) {
998
1145
  stateManager.complete();
999
1146
  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
- };
1147
+ finalMessage = message;
1148
+ break;
1015
1149
  }
1016
1150
  const parsedToolCalls = [];
1017
1151
  for (const tc of rawToolCalls) {
@@ -1041,6 +1175,24 @@ async function createSession({
1041
1175
  stateManager.updateUsage(result.usage);
1042
1176
  }
1043
1177
  }
1178
+ const rewind = toolCallResults.rewind;
1179
+ if (rewind) {
1180
+ workflow.log.info("rewinding turn", {
1181
+ agentName,
1182
+ threadId,
1183
+ turn: currentTurn,
1184
+ toolCallId: rewind.toolCallId,
1185
+ toolName: rewind.toolName
1186
+ });
1187
+ if (preAssistantLength === void 0) {
1188
+ throw workflow.ApplicationFailure.create({
1189
+ message: "Rewind requested but runAgent did not report `threadLengthAtCall`; the adapter must populate it to support rewinds.",
1190
+ nonRetryable: true
1191
+ });
1192
+ }
1193
+ await truncateThread(threadId, preAssistantLength, threadKey);
1194
+ continue;
1195
+ }
1044
1196
  if (stateManager.getStatus() === "WAITING_FOR_INPUT") {
1045
1197
  const conditionMet = await workflow.condition(
1046
1198
  () => stateManager.getStatus() === "RUNNING",
@@ -1083,11 +1235,21 @@ async function createSession({
1083
1235
  case "pause-until-parent-close":
1084
1236
  await sandboxOps.pauseSandbox(sandboxId);
1085
1237
  break;
1238
+ case "keep":
1239
+ case "keep-until-parent-close":
1240
+ break;
1241
+ case "snapshot":
1242
+ exitSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
1243
+ await sandboxOps.destroySandbox(sandboxId);
1244
+ break;
1086
1245
  }
1087
1246
  }
1088
1247
  if (destroySubagentSandboxes) {
1089
1248
  await destroySubagentSandboxes();
1090
1249
  }
1250
+ if (cleanupSubagentSnapshots) {
1251
+ await cleanupSubagentSnapshots();
1252
+ }
1091
1253
  }
1092
1254
  workflow.log.info("session ended", {
1093
1255
  agentName,
@@ -1095,14 +1257,18 @@ async function createSession({
1095
1257
  exitReason,
1096
1258
  turns: stateManager.getTurns(),
1097
1259
  durationMs: Date.now() - sessionStartMs,
1098
- usage: stateManager.getTotalUsage()
1260
+ usage: stateManager.getTotalUsage(),
1261
+ ...baseSnapshot && { hasBaseSnapshot: true },
1262
+ ...exitSnapshot && { hasExitSnapshot: true }
1099
1263
  });
1100
1264
  return {
1101
1265
  threadId,
1102
- finalMessage: null,
1266
+ finalMessage,
1103
1267
  exitReason,
1104
1268
  usage: stateManager.getTotalUsage(),
1105
- sandboxId
1269
+ sandboxId,
1270
+ ...baseSnapshot && { baseSnapshot },
1271
+ ...exitSnapshot && { snapshot: exitSnapshot }
1106
1272
  };
1107
1273
  }
1108
1274
  };
@@ -1391,44 +1557,16 @@ function defineSubagentWorkflow(config, fn) {
1391
1557
  ...workflowInput.thread && { thread: workflowInput.thread },
1392
1558
  ...workflowInput.sandbox && { sandbox: workflowInput.sandbox },
1393
1559
  onSandboxReady: (sandboxId) => {
1394
- void parentHandle.signal(childSandboxReadySignal, {
1395
- childWorkflowId: workflow.workflowInfo().workflowId,
1396
- sandboxId
1397
- });
1560
+ const isReuse = workflowInput.sandbox?.mode === "continue";
1561
+ if (!isReuse) {
1562
+ void parentHandle.signal(childSandboxReadySignal, {
1563
+ childWorkflowId: workflow.workflowInfo().workflowId,
1564
+ sandboxId
1565
+ });
1566
+ }
1398
1567
  }
1399
1568
  };
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;
1569
+ return fn(prompt, sessionInput, context ?? {});
1432
1570
  };
1433
1571
  Object.defineProperty(workflow$1, "name", { value: config.name });
1434
1572
  return Object.assign(workflow$1, {
@@ -1538,9 +1676,7 @@ function applyVirtualTreeMutations(stateManager, mutations) {
1538
1676
  tree = tree.filter((e) => e.path !== m.path);
1539
1677
  break;
1540
1678
  case "update":
1541
- tree = tree.map(
1542
- (e) => e.path === m.path ? { ...e, ...m.entry } : e
1543
- );
1679
+ tree = tree.map((e) => e.path === m.path ? { ...e, ...m.entry } : e);
1544
1680
  break;
1545
1681
  }
1546
1682
  }
@@ -1599,7 +1735,9 @@ function formatVirtualFileTree(entries, opts = {}) {
1599
1735
  // src/lib/virtual-fs/queries.ts
1600
1736
  function hasFileWithMimeType(stateManager, pattern) {
1601
1737
  const tree = stateManager.get("fileTree");
1602
- const matchers = (Array.isArray(pattern) ? pattern : [pattern]).map(buildMatcher);
1738
+ const matchers = (Array.isArray(pattern) ? pattern : [pattern]).map(
1739
+ buildMatcher
1740
+ );
1603
1741
  return tree.some((entry) => {
1604
1742
  const meta = entry.metadata;
1605
1743
  const mime = meta?.mimeType;
@@ -1660,7 +1798,9 @@ function proxyVirtualFsOps(scope, options) {
1660
1798
  // src/lib/skills/parse.ts
1661
1799
  function parseSkillFile(raw) {
1662
1800
  const trimmed = raw.replace(/^\uFEFF/, "");
1663
- const match = trimmed.match(/^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*\r?\n?([\s\S]*)$/);
1801
+ const match = trimmed.match(
1802
+ /^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*\r?\n?([\s\S]*)$/
1803
+ );
1664
1804
  if (!match) {
1665
1805
  throw new Error(
1666
1806
  "SKILL.md must start with YAML frontmatter delimited by ---"
@@ -1932,7 +2072,9 @@ function createTaskGetHandler(stateManager) {
1932
2072
  const task = stateManager.getTask(args.taskId) ?? null;
1933
2073
  if (!task) {
1934
2074
  return {
1935
- toolResponse: JSON.stringify({ error: `Task not found: ${args.taskId}` }),
2075
+ toolResponse: JSON.stringify({
2076
+ error: `Task not found: ${args.taskId}`
2077
+ }),
1936
2078
  data: null
1937
2079
  };
1938
2080
  }
@@ -1975,7 +2117,9 @@ function createTaskUpdateHandler(stateManager) {
1975
2117
  const task = stateManager.getTask(args.taskId);
1976
2118
  if (!task) {
1977
2119
  return {
1978
- toolResponse: JSON.stringify({ error: `Task not found: ${args.taskId}` }),
2120
+ toolResponse: JSON.stringify({
2121
+ error: `Task not found: ${args.taskId}`
2122
+ }),
1979
2123
  data: null
1980
2124
  };
1981
2125
  }
@@ -2085,6 +2229,8 @@ var FileSystemSkillProvider = class {
2085
2229
  this.fs = fs;
2086
2230
  this.baseDir = baseDir;
2087
2231
  }
2232
+ fs;
2233
+ baseDir;
2088
2234
  async listSkills() {
2089
2235
  const dirs = await this.discoverSkillDirs();
2090
2236
  const skills = [];
@@ -2100,9 +2246,7 @@ var FileSystemSkillProvider = class {
2100
2246
  return skills;
2101
2247
  }
2102
2248
  async getSkill(name) {
2103
- const raw = await this.fs.readFile(
2104
- path.join(this.baseDir, name, "SKILL.md")
2105
- );
2249
+ const raw = await this.fs.readFile(path.join(this.baseDir, name, "SKILL.md"));
2106
2250
  const { frontmatter, body } = parseSkillFile(raw);
2107
2251
  if (frontmatter.name !== name) {
2108
2252
  throw new Error(
@@ -2111,7 +2255,10 @@ var FileSystemSkillProvider = class {
2111
2255
  }
2112
2256
  const location = path.join(this.baseDir, name);
2113
2257
  const resourcePaths = await this.discoverResources(name);
2114
- const resourceContents = await this.readResourceContents(location, resourcePaths);
2258
+ const resourceContents = await this.readResourceContents(
2259
+ location,
2260
+ resourcePaths
2261
+ );
2115
2262
  return {
2116
2263
  ...frontmatter,
2117
2264
  instructions: body,
@@ -2131,7 +2278,10 @@ var FileSystemSkillProvider = class {
2131
2278
  const { frontmatter, body } = parseSkillFile(raw);
2132
2279
  const location = path.join(this.baseDir, dir);
2133
2280
  const resourcePaths = await this.discoverResources(dir);
2134
- const resourceContents = await this.readResourceContents(location, resourcePaths);
2281
+ const resourceContents = await this.readResourceContents(
2282
+ location,
2283
+ resourcePaths
2284
+ );
2135
2285
  skills.push({
2136
2286
  ...frontmatter,
2137
2287
  instructions: body,
@@ -2264,6 +2414,20 @@ function createThreadManager(config) {
2264
2414
  },
2265
2415
  async delete() {
2266
2416
  await redis.del(redisKey, metaKey);
2417
+ },
2418
+ async length() {
2419
+ await assertThreadExists();
2420
+ return redis.llen(redisKey);
2421
+ },
2422
+ async truncate(length) {
2423
+ await assertThreadExists();
2424
+ if (length <= 0) {
2425
+ await redis.del(redisKey);
2426
+ await redis.expire(metaKey, THREAD_TTL_SECONDS);
2427
+ } else {
2428
+ await redis.ltrim(redisKey, 0, length - 1);
2429
+ await redis.expire(redisKey, THREAD_TTL_SECONDS);
2430
+ }
2267
2431
  }
2268
2432
  };
2269
2433
  }
@@ -2305,6 +2469,7 @@ var SandboxManager = class {
2305
2469
  this.provider = provider;
2306
2470
  this.hooks = options?.hooks ?? {};
2307
2471
  }
2472
+ provider;
2308
2473
  hooks;
2309
2474
  async create(options, ctx) {
2310
2475
  let providerOptions = options;
@@ -2333,7 +2498,7 @@ var SandboxManager = class {
2333
2498
  }
2334
2499
  const { sandbox } = await this.provider.create(providerOptions);
2335
2500
  if (this.hooks.onPostCreate) {
2336
- await this.hooks.onPostCreate(sandbox.id);
2501
+ await this.hooks.onPostCreate(sandbox, ctx ?? {});
2337
2502
  }
2338
2503
  return { sandboxId: sandbox.id };
2339
2504
  }
@@ -2349,15 +2514,18 @@ var SandboxManager = class {
2349
2514
  async resume(id) {
2350
2515
  await this.provider.resume(id);
2351
2516
  }
2352
- async snapshot(id) {
2353
- return this.provider.snapshot(id);
2517
+ async snapshot(id, options) {
2518
+ return this.provider.snapshot(id, options);
2354
2519
  }
2355
- async restore(snapshot) {
2356
- const sandbox = await this.provider.restore(snapshot);
2520
+ async restore(snapshot, options) {
2521
+ const sandbox = await this.provider.restore(snapshot, options);
2357
2522
  return sandbox.id;
2358
2523
  }
2359
- async fork(sandboxId) {
2360
- const sandbox = await this.provider.fork(sandboxId);
2524
+ async deleteSnapshot(snapshot) {
2525
+ await this.provider.deleteSnapshot(snapshot);
2526
+ }
2527
+ async fork(sandboxId, options) {
2528
+ const sandbox = await this.provider.fork(sandboxId, options);
2361
2529
  return sandbox.id;
2362
2530
  }
2363
2531
  /**
@@ -2395,11 +2563,17 @@ var SandboxManager = class {
2395
2563
  resumeSandbox: async (sandboxId) => {
2396
2564
  await this.resume(sandboxId);
2397
2565
  },
2398
- snapshotSandbox: async (sandboxId) => {
2399
- return this.snapshot(sandboxId);
2566
+ snapshotSandbox: async (sandboxId, options) => {
2567
+ return this.snapshot(sandboxId, options);
2568
+ },
2569
+ restoreSandbox: async (snapshot, options) => {
2570
+ return this.restore(snapshot, options);
2571
+ },
2572
+ deleteSandboxSnapshot: async (snapshot) => {
2573
+ await this.deleteSnapshot(snapshot);
2400
2574
  },
2401
- forkSandbox: async (sandboxId) => {
2402
- return this.fork(sandboxId);
2575
+ forkSandbox: async (sandboxId, options) => {
2576
+ return this.fork(sandboxId, options);
2403
2577
  }
2404
2578
  };
2405
2579
  const cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
@@ -2513,6 +2687,8 @@ var VirtualFileSystem = class {
2513
2687
  ]) : []
2514
2688
  );
2515
2689
  }
2690
+ resolver;
2691
+ ctx;
2516
2692
  workspaceBase;
2517
2693
  entries;
2518
2694
  directories;