zeitlich 0.2.37 → 0.2.39

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 (172) hide show
  1. package/README.md +18 -0
  2. package/dist/{activities-Bb-nAjwQ.d.ts → activities-Bmu7XnaG.d.ts} +4 -4
  3. package/dist/{activities-vkI4_3CC.d.cts → activities-ByBFLvm2.d.cts} +4 -4
  4. package/dist/adapter-id-BB-mmrts.d.cts +17 -0
  5. package/dist/adapter-id-BB-mmrts.d.ts +17 -0
  6. package/dist/adapter-id-CMwVrVqv.d.cts +17 -0
  7. package/dist/adapter-id-CMwVrVqv.d.ts +17 -0
  8. package/dist/adapter-id-CbY2zeSt.d.cts +17 -0
  9. package/dist/adapter-id-CbY2zeSt.d.ts +17 -0
  10. package/dist/adapters/sandbox/bedrock/index.cjs +3 -3
  11. package/dist/adapters/sandbox/bedrock/index.cjs.map +1 -1
  12. package/dist/adapters/sandbox/bedrock/index.d.cts +6 -6
  13. package/dist/adapters/sandbox/bedrock/index.d.ts +6 -6
  14. package/dist/adapters/sandbox/bedrock/index.js +3 -3
  15. package/dist/adapters/sandbox/bedrock/index.js.map +1 -1
  16. package/dist/adapters/sandbox/bedrock/workflow.d.cts +2 -2
  17. package/dist/adapters/sandbox/bedrock/workflow.d.ts +2 -2
  18. package/dist/adapters/sandbox/daytona/index.cjs +3 -3
  19. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  20. package/dist/adapters/sandbox/daytona/index.d.cts +4 -4
  21. package/dist/adapters/sandbox/daytona/index.d.ts +4 -4
  22. package/dist/adapters/sandbox/daytona/index.js +3 -3
  23. package/dist/adapters/sandbox/daytona/index.js.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/e2b/index.cjs +26 -14
  27. package/dist/adapters/sandbox/e2b/index.cjs.map +1 -1
  28. package/dist/adapters/sandbox/e2b/index.d.cts +24 -4
  29. package/dist/adapters/sandbox/e2b/index.d.ts +24 -4
  30. package/dist/adapters/sandbox/e2b/index.js +26 -14
  31. package/dist/adapters/sandbox/e2b/index.js.map +1 -1
  32. package/dist/adapters/sandbox/e2b/workflow.d.cts +1 -1
  33. package/dist/adapters/sandbox/e2b/workflow.d.ts +1 -1
  34. package/dist/adapters/sandbox/inmemory/index.cjs +3 -3
  35. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
  36. package/dist/adapters/sandbox/inmemory/index.d.cts +4 -4
  37. package/dist/adapters/sandbox/inmemory/index.d.ts +4 -4
  38. package/dist/adapters/sandbox/inmemory/index.js +3 -3
  39. package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
  40. package/dist/adapters/sandbox/inmemory/workflow.d.cts +1 -1
  41. package/dist/adapters/sandbox/inmemory/workflow.d.ts +1 -1
  42. package/dist/adapters/thread/anthropic/index.cjs +150 -13
  43. package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
  44. package/dist/adapters/thread/anthropic/index.d.cts +9 -8
  45. package/dist/adapters/thread/anthropic/index.d.ts +9 -8
  46. package/dist/adapters/thread/anthropic/index.js +150 -14
  47. package/dist/adapters/thread/anthropic/index.js.map +1 -1
  48. package/dist/adapters/thread/anthropic/workflow.cjs +9 -3
  49. package/dist/adapters/thread/anthropic/workflow.cjs.map +1 -1
  50. package/dist/adapters/thread/anthropic/workflow.d.cts +6 -5
  51. package/dist/adapters/thread/anthropic/workflow.d.ts +6 -5
  52. package/dist/adapters/thread/anthropic/workflow.js +9 -4
  53. package/dist/adapters/thread/anthropic/workflow.js.map +1 -1
  54. package/dist/adapters/thread/google-genai/index.cjs +154 -13
  55. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  56. package/dist/adapters/thread/google-genai/index.d.cts +6 -5
  57. package/dist/adapters/thread/google-genai/index.d.ts +6 -5
  58. package/dist/adapters/thread/google-genai/index.js +154 -14
  59. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  60. package/dist/adapters/thread/google-genai/workflow.cjs +9 -3
  61. package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
  62. package/dist/adapters/thread/google-genai/workflow.d.cts +6 -5
  63. package/dist/adapters/thread/google-genai/workflow.d.ts +6 -5
  64. package/dist/adapters/thread/google-genai/workflow.js +9 -4
  65. package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
  66. package/dist/adapters/thread/index.cjs +16 -0
  67. package/dist/adapters/thread/index.cjs.map +1 -0
  68. package/dist/adapters/thread/index.d.cts +34 -0
  69. package/dist/adapters/thread/index.d.ts +34 -0
  70. package/dist/adapters/thread/index.js +12 -0
  71. package/dist/adapters/thread/index.js.map +1 -0
  72. package/dist/adapters/thread/langchain/index.cjs +149 -14
  73. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  74. package/dist/adapters/thread/langchain/index.d.cts +9 -8
  75. package/dist/adapters/thread/langchain/index.d.ts +9 -8
  76. package/dist/adapters/thread/langchain/index.js +149 -15
  77. package/dist/adapters/thread/langchain/index.js.map +1 -1
  78. package/dist/adapters/thread/langchain/workflow.cjs +9 -3
  79. package/dist/adapters/thread/langchain/workflow.cjs.map +1 -1
  80. package/dist/adapters/thread/langchain/workflow.d.cts +6 -5
  81. package/dist/adapters/thread/langchain/workflow.d.ts +6 -5
  82. package/dist/adapters/thread/langchain/workflow.js +9 -4
  83. package/dist/adapters/thread/langchain/workflow.js.map +1 -1
  84. package/dist/index.cjs +367 -59
  85. package/dist/index.cjs.map +1 -1
  86. package/dist/index.d.cts +11 -11
  87. package/dist/index.d.ts +11 -11
  88. package/dist/index.js +365 -61
  89. package/dist/index.js.map +1 -1
  90. package/dist/{proxy-DEtowJyd.d.cts → proxy-BAKzNGRq.d.cts} +1 -1
  91. package/dist/{proxy-0smGKvx8.d.ts → proxy-DO_MXbY4.d.ts} +1 -1
  92. package/dist/{thread-manager-C-C4pI2z.d.ts → thread-manager-CcRXasqs.d.ts} +2 -2
  93. package/dist/{thread-manager-D4vgzYrh.d.cts → thread-manager-ClwSaUnj.d.cts} +2 -2
  94. package/dist/{thread-manager-3fszQih4.d.ts → thread-manager-D-7lp1JK.d.ts} +2 -2
  95. package/dist/{thread-manager-CzYln2OC.d.cts → thread-manager-Y8Ucf0Tf.d.cts} +2 -2
  96. package/dist/{types-CPKDl-y_.d.ts → types-Bcbiq8iv.d.cts} +195 -22
  97. package/dist/{types-CNuWnvy9.d.ts → types-DAsQ21Rt.d.ts} +1 -1
  98. package/dist/{types-B37hKoWA.d.ts → types-DpHTX-iO.d.ts} +58 -1
  99. package/dist/{types-BO7Yju20.d.cts → types-Dt8-HBBT.d.ts} +195 -22
  100. package/dist/{types-D08CXPh8.d.cts → types-hFFi-Zd9.d.cts} +58 -1
  101. package/dist/{types-DWEUmYAJ.d.cts → types-lm8tMNJQ.d.cts} +1 -1
  102. package/dist/{types-tQL9njTu.d.cts → types-yx0LzPGn.d.cts} +21 -7
  103. package/dist/{types-tQL9njTu.d.ts → types-yx0LzPGn.d.ts} +21 -7
  104. package/dist/{workflow-CjXHbZZc.d.ts → workflow-Bmf9EtDW.d.ts} +83 -3
  105. package/dist/{workflow-Do_lzJpT.d.cts → workflow-Bx9utBwb.d.cts} +83 -3
  106. package/dist/workflow.cjs +266 -39
  107. package/dist/workflow.cjs.map +1 -1
  108. package/dist/workflow.d.cts +3 -3
  109. package/dist/workflow.d.ts +3 -3
  110. package/dist/workflow.js +264 -41
  111. package/dist/workflow.js.map +1 -1
  112. package/package.json +12 -2
  113. package/src/adapters/sandbox/bedrock/index.ts +12 -3
  114. package/src/adapters/sandbox/daytona/index.ts +12 -3
  115. package/src/adapters/sandbox/e2b/index.ts +36 -14
  116. package/src/adapters/sandbox/e2b/types.ts +16 -0
  117. package/src/adapters/sandbox/inmemory/index.ts +12 -3
  118. package/src/adapters/thread/adapter-id.test.ts +42 -0
  119. package/src/adapters/thread/anthropic/activities.ts +40 -5
  120. package/src/adapters/thread/anthropic/adapter-id.ts +16 -0
  121. package/src/adapters/thread/anthropic/fork-transform.test.ts +291 -0
  122. package/src/adapters/thread/anthropic/index.ts +3 -0
  123. package/src/adapters/thread/anthropic/model-invoker.ts +7 -1
  124. package/src/adapters/thread/anthropic/proxy.ts +3 -2
  125. package/src/adapters/thread/anthropic/thread-manager.ts +27 -1
  126. package/src/adapters/thread/google-genai/activities.ts +44 -5
  127. package/src/adapters/thread/google-genai/adapter-id.ts +16 -0
  128. package/src/adapters/thread/google-genai/fork-transform.test.ts +149 -0
  129. package/src/adapters/thread/google-genai/index.ts +3 -0
  130. package/src/adapters/thread/google-genai/model-invoker.ts +8 -2
  131. package/src/adapters/thread/google-genai/proxy.ts +3 -2
  132. package/src/adapters/thread/google-genai/thread-manager.ts +27 -1
  133. package/src/adapters/thread/index.ts +39 -0
  134. package/src/adapters/thread/langchain/activities.ts +40 -5
  135. package/src/adapters/thread/langchain/adapter-id.ts +16 -0
  136. package/src/adapters/thread/langchain/fork-transform.test.ts +142 -0
  137. package/src/adapters/thread/langchain/index.ts +3 -0
  138. package/src/adapters/thread/langchain/model-invoker.ts +7 -1
  139. package/src/adapters/thread/langchain/proxy.ts +3 -2
  140. package/src/adapters/thread/langchain/thread-manager.ts +27 -1
  141. package/src/lib/lifecycle.ts +14 -5
  142. package/src/lib/model/types.ts +7 -0
  143. package/src/lib/sandbox/manager.ts +26 -18
  144. package/src/lib/sandbox/types.ts +27 -7
  145. package/src/lib/session/session-edge-cases.integration.test.ts +336 -4
  146. package/src/lib/session/session.integration.test.ts +192 -2
  147. package/src/lib/session/session.ts +102 -8
  148. package/src/lib/session/types.ts +66 -3
  149. package/src/lib/state/index.ts +1 -0
  150. package/src/lib/state/manager.integration.test.ts +109 -0
  151. package/src/lib/state/manager.ts +38 -8
  152. package/src/lib/state/types.ts +25 -0
  153. package/src/lib/subagent/handler.ts +124 -11
  154. package/src/lib/subagent/index.ts +5 -1
  155. package/src/lib/subagent/subagent.integration.test.ts +628 -104
  156. package/src/lib/subagent/types.ts +63 -14
  157. package/src/lib/subagent/workflow.ts +29 -2
  158. package/src/lib/thread/index.ts +5 -0
  159. package/src/lib/thread/keys.test.ts +101 -0
  160. package/src/lib/thread/keys.ts +94 -0
  161. package/src/lib/thread/manager.test.ts +139 -0
  162. package/src/lib/thread/manager.ts +105 -9
  163. package/src/lib/thread/proxy.ts +3 -0
  164. package/src/lib/thread/types.ts +64 -1
  165. package/src/lib/tool-router/index.ts +2 -0
  166. package/src/lib/tool-router/router-edge-cases.integration.test.ts +92 -0
  167. package/src/lib/tool-router/router.integration.test.ts +12 -0
  168. package/src/lib/tool-router/router.ts +89 -16
  169. package/src/lib/tool-router/types.ts +42 -1
  170. package/src/lib/types.ts +12 -0
  171. package/src/workflow.ts +14 -1
  172. package/tsup.config.ts +1 -0
package/dist/workflow.cjs CHANGED
@@ -113,7 +113,7 @@ function createToolRouter(options) {
113
113
  });
114
114
  }
115
115
  }
116
- async function processToolCall(toolCall, turn, sandboxId) {
116
+ async function processToolCall(toolCall, turn, sandboxId, onRewindRequested) {
117
117
  const startTime = Date.now();
118
118
  const tool = toolMap.get(toolCall.name);
119
119
  const preResult = await runPreHooks(toolCall, tool, turn);
@@ -128,7 +128,7 @@ function createToolRouter(options) {
128
128
  reason: "Skipped by PreToolUse hook"
129
129
  })
130
130
  });
131
- return null;
131
+ return { kind: "skipped" };
132
132
  }
133
133
  const effectiveArgs = preResult.args;
134
134
  workflow.log.debug("tool call dispatched", {
@@ -140,6 +140,7 @@ function createToolRouter(options) {
140
140
  let content;
141
141
  let resultAppended = false;
142
142
  let metadata;
143
+ let rewindRequested = false;
143
144
  try {
144
145
  if (tool) {
145
146
  const routerContext = {
@@ -157,11 +158,15 @@ function createToolRouter(options) {
157
158
  content = response.toolResponse;
158
159
  resultAppended = response.resultAppended === true;
159
160
  metadata = response.metadata;
161
+ rewindRequested = response.rewind === true;
160
162
  } else {
161
163
  result = { error: `Unknown tool: ${toolCall.name}` };
162
164
  content = JSON.stringify(result, null, 2);
163
165
  }
164
166
  } catch (error) {
167
+ if (workflow.isCancellation(error)) {
168
+ throw error;
169
+ }
165
170
  workflow.log.warn("tool call failed", {
166
171
  toolName: toolCall.name,
167
172
  toolCallId: toolCall.id,
@@ -179,6 +184,15 @@ function createToolRouter(options) {
179
184
  result = recovery.result;
180
185
  content = recovery.content;
181
186
  }
187
+ if (rewindRequested) {
188
+ const signal = {
189
+ toolCallId: toolCall.id,
190
+ toolName: toolCall.name
191
+ };
192
+ workflow.log.info("tool requested rewind", { ...signal });
193
+ onRewindRequested?.(signal);
194
+ return { kind: "rewind", signal };
195
+ }
182
196
  if (!resultAppended) {
183
197
  const config = {
184
198
  threadId: options.threadId,
@@ -215,7 +229,7 @@ function createToolRouter(options) {
215
229
  turn,
216
230
  durationMs
217
231
  );
218
- return toolResult;
232
+ return { kind: "result", value: toolResult };
219
233
  }
220
234
  return {
221
235
  hasTools() {
@@ -250,27 +264,59 @@ function createToolRouter(options) {
250
264
  }));
251
265
  },
252
266
  async processToolCalls(toolCalls, context) {
267
+ const attachRewind = (arr, rewind) => {
268
+ if (rewind) {
269
+ arr.rewind = rewind;
270
+ }
271
+ return arr;
272
+ };
253
273
  if (toolCalls.length === 0) {
254
- return [];
274
+ return attachRewind([], void 0);
255
275
  }
256
276
  const turn = context?.turn ?? 0;
257
277
  const sandboxId = context?.sandboxId;
278
+ let rewindSignal;
258
279
  if (options.parallel) {
259
- const results2 = await Promise.all(
260
- toolCalls.map((tc) => processToolCall(tc, turn, sandboxId))
261
- );
262
- return results2.filter(
263
- (r) => r !== null
280
+ const scope = new workflow.CancellationScope({ cancellable: true });
281
+ const onRewindRequested = (signal) => {
282
+ if (!rewindSignal) {
283
+ rewindSignal = signal;
284
+ scope.cancel();
285
+ }
286
+ };
287
+ const outcomes = await scope.run(
288
+ async () => Promise.allSettled(
289
+ toolCalls.map(
290
+ (tc) => processToolCall(tc, turn, sandboxId, onRewindRequested)
291
+ )
292
+ )
264
293
  );
294
+ const results2 = [];
295
+ for (const outcome of outcomes) {
296
+ if (outcome.status === "rejected") {
297
+ if (workflow.isCancellation(outcome.reason)) {
298
+ continue;
299
+ }
300
+ throw outcome.reason;
301
+ }
302
+ if (outcome.value.kind === "result") {
303
+ results2.push(outcome.value.value);
304
+ }
305
+ }
306
+ return attachRewind(results2, rewindSignal);
265
307
  }
266
308
  const results = [];
267
309
  for (const toolCall of toolCalls) {
268
- const result = await processToolCall(toolCall, turn, sandboxId);
269
- if (result !== null) {
270
- results.push(result);
310
+ const outcome = await processToolCall(toolCall, turn, sandboxId);
311
+ if (outcome.kind === "rewind") {
312
+ rewindSignal = outcome.signal;
313
+ break;
314
+ }
315
+ if (outcome.kind === "result") {
316
+ results.push(outcome.value);
271
317
  }
272
318
  }
273
- return results;
319
+ return attachRewind(results, rewindSignal);
274
320
  },
275
321
  async processToolCallsByName(toolCalls, toolName, handler, context) {
276
322
  const matchingCalls = toolCalls.filter((tc) => tc.name === toolName);
@@ -394,6 +440,7 @@ function createSubagentTool(subagents) {
394
440
  var childSandboxReadySignal = workflow.defineSignal("childSandboxReady");
395
441
 
396
442
  // src/lib/subagent/handler.ts
443
+ var DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT = "1h";
397
444
  function resolveSandboxConfig(config) {
398
445
  if (!config || config === "none") {
399
446
  return { source: "none", init: "per-call", continuation: "fork" };
@@ -425,17 +472,28 @@ function createSubagentHandler(subagents) {
425
472
  const threadSandboxes = /* @__PURE__ */ new Map();
426
473
  const persistentSandboxes = /* @__PURE__ */ new Map();
427
474
  const persistentSandboxCreating = /* @__PURE__ */ new Set();
475
+ const persistentSandboxCreationError = /* @__PURE__ */ new Map();
428
476
  const lazyCreatorAgent = /* @__PURE__ */ new Map();
477
+ const snapshotBaseCreatorAgent = /* @__PURE__ */ new Map();
429
478
  const threadSnapshots = /* @__PURE__ */ new Map();
430
479
  const persistentBaseSnapshot = /* @__PURE__ */ new Map();
431
480
  const persistentBaseSnapshotCreating = /* @__PURE__ */ new Set();
432
- workflow.setHandler(childSandboxReadySignal, ({ childWorkflowId, sandboxId }) => {
433
- const agentName = lazyCreatorAgent.get(childWorkflowId);
434
- if (agentName && !persistentSandboxes.has(agentName)) {
435
- persistentSandboxes.set(agentName, sandboxId);
436
- lazyCreatorAgent.delete(childWorkflowId);
481
+ const persistentBaseSnapshotCreationError = /* @__PURE__ */ new Map();
482
+ workflow.setHandler(
483
+ childSandboxReadySignal,
484
+ ({ childWorkflowId, sandboxId, baseSnapshot }) => {
485
+ const lazyAgent = lazyCreatorAgent.get(childWorkflowId);
486
+ if (lazyAgent && !persistentSandboxes.has(lazyAgent)) {
487
+ persistentSandboxes.set(lazyAgent, sandboxId);
488
+ lazyCreatorAgent.delete(childWorkflowId);
489
+ }
490
+ const snapAgent = snapshotBaseCreatorAgent.get(childWorkflowId);
491
+ if (snapAgent && baseSnapshot && !persistentBaseSnapshot.has(snapAgent)) {
492
+ persistentBaseSnapshot.set(snapAgent, baseSnapshot);
493
+ snapshotBaseCreatorAgent.delete(childWorkflowId);
494
+ }
437
495
  }
438
- });
496
+ );
439
497
  const handler = async (args, context) => {
440
498
  const config = subagents.find((s) => s.agentName === args.subagent);
441
499
  if (!config) {
@@ -491,8 +549,20 @@ function createSubagentHandler(subagents) {
491
549
  baseSnap = persistentBaseSnapshot.get(config.agentName);
492
550
  if (!baseSnap) {
493
551
  if (persistentBaseSnapshotCreating.has(config.agentName)) {
494
- await workflow.condition(() => persistentBaseSnapshot.has(config.agentName));
552
+ await workflow.condition(
553
+ () => persistentBaseSnapshot.has(config.agentName) || persistentBaseSnapshotCreationError.has(config.agentName) || !persistentBaseSnapshotCreating.has(config.agentName)
554
+ );
555
+ const creatorErr = persistentBaseSnapshotCreationError.get(
556
+ config.agentName
557
+ );
558
+ if (creatorErr !== void 0) {
559
+ throw creatorErr;
560
+ }
495
561
  baseSnap = persistentBaseSnapshot.get(config.agentName);
562
+ if (!baseSnap) {
563
+ persistentBaseSnapshotCreating.add(config.agentName);
564
+ isSnapshotBaseCreator = true;
565
+ }
496
566
  } else {
497
567
  persistentBaseSnapshotCreating.add(config.agentName);
498
568
  isSnapshotBaseCreator = true;
@@ -510,8 +580,20 @@ function createSubagentHandler(subagents) {
510
580
  baseSandboxId = persistentSandboxes.get(config.agentName);
511
581
  if (!baseSandboxId) {
512
582
  if (persistentSandboxCreating.has(config.agentName)) {
513
- await workflow.condition(() => persistentSandboxes.has(config.agentName));
583
+ await workflow.condition(
584
+ () => persistentSandboxes.has(config.agentName) || persistentSandboxCreationError.has(config.agentName) || !persistentSandboxCreating.has(config.agentName)
585
+ );
586
+ const creatorErr = persistentSandboxCreationError.get(
587
+ config.agentName
588
+ );
589
+ if (creatorErr !== void 0) {
590
+ throw creatorErr;
591
+ }
514
592
  baseSandboxId = persistentSandboxes.get(config.agentName);
593
+ if (!baseSandboxId) {
594
+ persistentSandboxCreating.add(config.agentName);
595
+ isLazyCreator = true;
596
+ }
515
597
  } else {
516
598
  persistentSandboxCreating.add(config.agentName);
517
599
  isLazyCreator = true;
@@ -540,6 +622,12 @@ function createSubagentHandler(subagents) {
540
622
  };
541
623
  const resolvedContext = config.context === void 0 ? void 0 : typeof config.context === "function" ? config.context() : config.context;
542
624
  const childOpts = {
625
+ // Apply a bounded run timeout by default so a child workflow that
626
+ // fails to initialize or otherwise never reaches a terminal state
627
+ // cannot hang the parent's `Subagent` tool call forever. Callers can
628
+ // raise, lower, or disable it via `workflowOptions.workflowRunTimeout`.
629
+ workflowRunTimeout: DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT,
630
+ ...config.workflowOptions ?? {},
543
631
  workflowId: childWorkflowId,
544
632
  args: resolvedContext === void 0 ? [args.prompt, workflowInput] : [args.prompt, workflowInput, resolvedContext],
545
633
  taskQueue: config.taskQueue ?? parentTaskQueue
@@ -547,13 +635,39 @@ function createSubagentHandler(subagents) {
547
635
  if (isLazyCreator) {
548
636
  lazyCreatorAgent.set(childWorkflowId, config.agentName);
549
637
  }
638
+ if (isSnapshotBaseCreator) {
639
+ snapshotBaseCreatorAgent.set(childWorkflowId, config.agentName);
640
+ }
550
641
  workflow.log.info("subagent spawned", {
551
642
  subagent: config.agentName,
552
643
  childWorkflowId,
553
644
  threadMode,
554
645
  sandboxSource: sandboxCfg.source
555
646
  });
556
- const childResult = await workflow.executeChild(config.workflow, childOpts);
647
+ let childResult;
648
+ try {
649
+ childResult = await workflow.executeChild(
650
+ config.workflow,
651
+ childOpts
652
+ );
653
+ } catch (err) {
654
+ workflow.log.warn("subagent failed", {
655
+ subagent: config.agentName,
656
+ childWorkflowId,
657
+ error: err instanceof Error ? err.message : String(err)
658
+ });
659
+ if (isLazyCreator) {
660
+ persistentSandboxCreating.delete(config.agentName);
661
+ persistentSandboxCreationError.set(config.agentName, err);
662
+ lazyCreatorAgent.delete(childWorkflowId);
663
+ }
664
+ if (isSnapshotBaseCreator) {
665
+ persistentBaseSnapshotCreating.delete(config.agentName);
666
+ persistentBaseSnapshotCreationError.set(config.agentName, err);
667
+ snapshotBaseCreatorAgent.delete(childWorkflowId);
668
+ }
669
+ throw err;
670
+ }
557
671
  const effectiveShutdown = sandboxShutdownOverride ?? sandboxCfg.shutdown ?? "destroy";
558
672
  workflow.log.info("subagent completed", {
559
673
  subagent: config.agentName,
@@ -597,10 +711,13 @@ function createSubagentHandler(subagents) {
597
711
  }
598
712
  if (isLazyCreator) {
599
713
  persistentSandboxCreating.delete(config.agentName);
714
+ persistentSandboxCreationError.delete(config.agentName);
600
715
  lazyCreatorAgent.delete(childWorkflowId);
601
716
  }
602
717
  if (isSnapshotBaseCreator) {
603
718
  persistentBaseSnapshotCreating.delete(config.agentName);
719
+ persistentBaseSnapshotCreationError.delete(config.agentName);
720
+ snapshotBaseCreatorAgent.delete(childWorkflowId);
604
721
  }
605
722
  if (!toolResponse) {
606
723
  return {
@@ -843,6 +960,7 @@ async function createSession({
843
960
  sandbox: sandboxInit,
844
961
  sandboxShutdown = "destroy",
845
962
  onSandboxReady,
963
+ onSessionExit,
846
964
  virtualFs: virtualFsConfig,
847
965
  virtualFsOps
848
966
  }) {
@@ -867,7 +985,9 @@ async function createSession({
867
985
  initializeThread,
868
986
  appendSystemMessage,
869
987
  appendAgentMessage,
870
- forkThread
988
+ forkThread,
989
+ loadThreadState,
990
+ saveThreadState
871
991
  } = threadOps;
872
992
  const plugins = [];
873
993
  let destroySubagentSandboxes;
@@ -961,8 +1081,10 @@ async function createSession({
961
1081
  nonRetryable: true
962
1082
  });
963
1083
  }
1084
+ const forkInit = sandboxInit;
964
1085
  sandboxId = await sandboxOps.forkSandbox(
965
- sandboxInit.sandboxId
1086
+ forkInit.sandboxId,
1087
+ forkInit.options
966
1088
  );
967
1089
  sandboxOwned = true;
968
1090
  } else if (sandboxMode === "from-snapshot") {
@@ -972,8 +1094,11 @@ async function createSession({
972
1094
  nonRetryable: true
973
1095
  });
974
1096
  }
975
- const snap = sandboxInit.snapshot;
976
- sandboxId = await sandboxOps.restoreSandbox(snap);
1097
+ const restoreInit = sandboxInit;
1098
+ sandboxId = await sandboxOps.restoreSandbox(
1099
+ restoreInit.snapshot,
1100
+ restoreInit.options
1101
+ );
977
1102
  sandboxOwned = true;
978
1103
  } else if (sandboxOps) {
979
1104
  const skillFiles = skills ? collectSkillFiles(skills) : void 0;
@@ -990,7 +1115,10 @@ async function createSession({
990
1115
  baseSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
991
1116
  }
992
1117
  if (sandboxId && sandboxOwned && onSandboxReady) {
993
- onSandboxReady(sandboxId);
1118
+ onSandboxReady({
1119
+ sandboxId,
1120
+ ...baseSnapshot && { baseSnapshot }
1121
+ });
994
1122
  }
995
1123
  if (virtualFsConfig) {
996
1124
  if (!virtualFsOps) {
@@ -1033,9 +1161,20 @@ async function createSession({
1033
1161
  });
1034
1162
  const sessionStartMs = Date.now();
1035
1163
  const systemPrompt = stateManager.getSystemPrompt();
1164
+ const rehydrateFromSlice = (slice) => {
1165
+ stateManager.mergeUpdate({
1166
+ tasks: new Map(slice.tasks),
1167
+ ...slice.custom
1168
+ });
1169
+ };
1036
1170
  if (threadMode === "fork" && sourceThreadId) {
1037
1171
  await forkThread(sourceThreadId, threadId, threadKey);
1038
- } else if (threadMode === "continue") ; else {
1172
+ const forkedSlice = await loadThreadState(threadId, threadKey);
1173
+ if (forkedSlice) rehydrateFromSlice(forkedSlice);
1174
+ } else if (threadMode === "continue") {
1175
+ const continuedSlice = await loadThreadState(threadId, threadKey);
1176
+ if (continuedSlice) rehydrateFromSlice(continuedSlice);
1177
+ } else {
1039
1178
  if (appendSystemPrompt) {
1040
1179
  if (systemPrompt == null || typeof systemPrompt === "string" && systemPrompt.trim() === "") {
1041
1180
  throw workflow.ApplicationFailure.create({
@@ -1057,18 +1196,21 @@ async function createSession({
1057
1196
  let exitReason = "completed";
1058
1197
  let finalMessage = null;
1059
1198
  try {
1199
+ let assistantId;
1060
1200
  while (stateManager.isRunning() && !stateManager.isTerminal() && stateManager.getTurns() < maxTurns) {
1061
1201
  stateManager.incrementTurns();
1062
1202
  const currentTurn = stateManager.getTurns();
1063
1203
  workflow.log.debug("turn started", { agentName, threadId, turn: currentTurn });
1064
1204
  stateManager.setTools(toolRouter.getToolDefinitions());
1205
+ assistantId ??= workflow.uuid4();
1065
1206
  const { message, rawToolCalls, usage } = await runAgent({
1066
1207
  threadId,
1067
1208
  threadKey,
1068
1209
  agentName,
1069
- metadata
1210
+ metadata,
1211
+ assistantMessageId: assistantId
1070
1212
  });
1071
- await appendAgentMessage(threadId, workflow.uuid4(), message, threadKey);
1213
+ await appendAgentMessage(threadId, assistantId, message, threadKey);
1072
1214
  if (usage) {
1073
1215
  stateManager.updateUsage(usage);
1074
1216
  }
@@ -1113,6 +1255,18 @@ async function createSession({
1113
1255
  stateManager.updateUsage(result.usage);
1114
1256
  }
1115
1257
  }
1258
+ const rewind = toolCallResults.rewind;
1259
+ if (rewind) {
1260
+ workflow.log.info("rewinding turn", {
1261
+ agentName,
1262
+ threadId,
1263
+ turn: currentTurn,
1264
+ toolCallId: rewind.toolCallId,
1265
+ toolName: rewind.toolName
1266
+ });
1267
+ continue;
1268
+ }
1269
+ assistantId = void 0;
1116
1270
  if (stateManager.getStatus() === "WAITING_FOR_INPUT") {
1117
1271
  const conditionMet = await workflow.condition(
1118
1272
  () => stateManager.getStatus() === "RUNNING",
@@ -1145,6 +1299,19 @@ async function createSession({
1145
1299
  });
1146
1300
  throw workflow.ApplicationFailure.fromError(error);
1147
1301
  } finally {
1302
+ try {
1303
+ await saveThreadState(
1304
+ threadId,
1305
+ stateManager.getPersistedSlice(),
1306
+ threadKey
1307
+ );
1308
+ } catch (persistError) {
1309
+ workflow.log.warn("failed to persist thread state", {
1310
+ agentName,
1311
+ threadId,
1312
+ error: persistError instanceof Error ? persistError.message : String(persistError)
1313
+ });
1314
+ }
1148
1315
  await callSessionEnd(exitReason, stateManager.getTurns());
1149
1316
  if (sandboxOwned && sandboxId && sandboxOps) {
1150
1317
  switch (sandboxShutdown) {
@@ -1181,6 +1348,12 @@ async function createSession({
1181
1348
  ...baseSnapshot && { hasBaseSnapshot: true },
1182
1349
  ...exitSnapshot && { hasExitSnapshot: true }
1183
1350
  });
1351
+ if (onSessionExit) {
1352
+ onSessionExit({
1353
+ ...sandboxId && { sandboxId },
1354
+ ...exitSnapshot && { snapshot: exitSnapshot }
1355
+ });
1356
+ }
1184
1357
  return {
1185
1358
  threadId,
1186
1359
  finalMessage,
@@ -1209,6 +1382,15 @@ function defineWorkflow(config, fn) {
1209
1382
  return workflow;
1210
1383
  }
1211
1384
 
1385
+ // src/lib/thread/keys.ts
1386
+ var THREAD_TTL_SECONDS = 60 * 60 * 24 * 90;
1387
+ function getThreadListKey(threadKey, threadId) {
1388
+ return `${threadKey}:thread:${threadId}`;
1389
+ }
1390
+ function getThreadMetaKey(threadKey, threadId) {
1391
+ return `${threadKey}:meta:thread:${threadId}`;
1392
+ }
1393
+
1212
1394
  // src/lib/types.ts
1213
1395
  function isTerminalStatus(status) {
1214
1396
  return status === "COMPLETED" || status === "FAILED" || status === "CANCELLED";
@@ -1228,11 +1410,19 @@ function createAgentStateManager({
1228
1410
  let systemPrompt = initialState?.systemPrompt;
1229
1411
  const tasks = new Map(initialState?.tasks);
1230
1412
  const {
1231
- status: _,
1232
- version: __,
1233
- turns: ___,
1234
- tasks: ____,
1235
- tools: _____,
1413
+ status: _status,
1414
+ version: _version,
1415
+ turns: _turns,
1416
+ tasks: _tasks,
1417
+ tools: _tools,
1418
+ systemPrompt: _systemPrompt,
1419
+ fileTree: _fileTree,
1420
+ inlineFiles: _inlineFiles,
1421
+ virtualFsCtx: _virtualFsCtx,
1422
+ totalInputTokens: _totalInputTokens,
1423
+ totalOutputTokens: _totalOutputTokens,
1424
+ cachedWriteTokens: _cachedWriteTokens,
1425
+ cachedReadTokens: _cachedReadTokens,
1236
1426
  ...custom
1237
1427
  } = initialState ?? {};
1238
1428
  const customState = custom;
@@ -1312,7 +1502,14 @@ function createAgentStateManager({
1312
1502
  version++;
1313
1503
  },
1314
1504
  mergeUpdate(update) {
1315
- Object.assign(customState, update);
1505
+ const { tasks: nextTasks, ...rest } = update;
1506
+ if (nextTasks) {
1507
+ tasks.clear();
1508
+ for (const [id, task] of nextTasks) {
1509
+ tasks.set(id, task);
1510
+ }
1511
+ }
1512
+ Object.assign(customState, rest);
1316
1513
  version++;
1317
1514
  },
1318
1515
  getCurrentState() {
@@ -1350,6 +1547,12 @@ function createAgentStateManager({
1350
1547
  }
1351
1548
  return deleted;
1352
1549
  },
1550
+ getPersistedSlice() {
1551
+ return {
1552
+ tasks: Array.from(tasks.entries()),
1553
+ custom: { ...customState }
1554
+ };
1555
+ },
1353
1556
  updateUsage(usage) {
1354
1557
  totalInputTokens += usage.inputTokens ?? 0;
1355
1558
  totalOutputTokens += usage.outputTokens ?? 0;
@@ -1393,22 +1596,42 @@ function defineSubagentWorkflow(config, fn) {
1393
1596
  });
1394
1597
  }
1395
1598
  const parentHandle = workflow.getExternalWorkflowHandle(parent.workflowId);
1599
+ let capturedSandboxId;
1600
+ let capturedSnapshot;
1601
+ let capturedBaseSnapshot;
1602
+ let capturedThreadId;
1396
1603
  const sessionInput = {
1397
1604
  agentName: config.name,
1398
1605
  sandboxShutdown: effectiveShutdown,
1399
1606
  ...workflowInput.thread && { thread: workflowInput.thread },
1400
1607
  ...workflowInput.sandbox && { sandbox: workflowInput.sandbox },
1401
- onSandboxReady: (sandboxId) => {
1608
+ onSandboxReady: ({ sandboxId, baseSnapshot }) => {
1609
+ capturedBaseSnapshot = baseSnapshot;
1402
1610
  const isReuse = workflowInput.sandbox?.mode === "continue";
1403
1611
  if (!isReuse) {
1404
1612
  void parentHandle.signal(childSandboxReadySignal, {
1405
1613
  childWorkflowId: workflow.workflowInfo().workflowId,
1406
- sandboxId
1614
+ sandboxId,
1615
+ ...baseSnapshot && { baseSnapshot }
1407
1616
  });
1408
1617
  }
1618
+ },
1619
+ onSessionExit: ({ sandboxId, snapshot, threadId }) => {
1620
+ capturedSandboxId = sandboxId;
1621
+ capturedSnapshot = snapshot;
1622
+ capturedThreadId = threadId;
1623
+ }
1624
+ };
1625
+ const result = await fn(prompt, sessionInput, context ?? {});
1626
+ return {
1627
+ ...result,
1628
+ ...capturedThreadId !== void 0 && { threadId: capturedThreadId },
1629
+ ...capturedSandboxId !== void 0 && { sandboxId: capturedSandboxId },
1630
+ ...capturedSnapshot !== void 0 && { snapshot: capturedSnapshot },
1631
+ ...capturedBaseSnapshot !== void 0 && {
1632
+ baseSnapshot: capturedBaseSnapshot
1409
1633
  }
1410
1634
  };
1411
- return fn(prompt, sessionInput, context ?? {});
1412
1635
  };
1413
1636
  Object.defineProperty(workflow$1, "name", { value: config.name });
1414
1637
  return Object.assign(workflow$1, {
@@ -2067,8 +2290,10 @@ var createAskUserQuestionHandler = () => async (args) => {
2067
2290
  };
2068
2291
  };
2069
2292
 
2293
+ exports.DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT = DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT;
2070
2294
  exports.SandboxNotFoundError = SandboxNotFoundError;
2071
2295
  exports.SandboxNotSupportedError = SandboxNotSupportedError;
2296
+ exports.THREAD_TTL_SECONDS = THREAD_TTL_SECONDS;
2072
2297
  exports.applyVirtualTreeMutations = applyVirtualTreeMutations;
2073
2298
  exports.askUserQuestionTool = askUserQuestionTool;
2074
2299
  exports.bashTool = bashTool;
@@ -2093,6 +2318,8 @@ exports.editTool = editTool;
2093
2318
  exports.filesWithMimeType = filesWithMimeType;
2094
2319
  exports.formatVirtualFileTree = formatVirtualFileTree;
2095
2320
  exports.getShortId = getShortId;
2321
+ exports.getThreadListKey = getThreadListKey;
2322
+ exports.getThreadMetaKey = getThreadMetaKey;
2096
2323
  exports.globTool = globTool;
2097
2324
  exports.grepTool = grepTool;
2098
2325
  exports.hasDirectory = hasDirectory;