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/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);
@@ -398,6 +444,7 @@ function createSubagentTool(subagents) {
398
444
  var childSandboxReadySignal = workflow.defineSignal("childSandboxReady");
399
445
 
400
446
  // src/lib/subagent/handler.ts
447
+ var DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT = "1h";
401
448
  function resolveSandboxConfig(config) {
402
449
  if (!config || config === "none") {
403
450
  return { source: "none", init: "per-call", continuation: "fork" };
@@ -429,17 +476,28 @@ function createSubagentHandler(subagents) {
429
476
  const threadSandboxes = /* @__PURE__ */ new Map();
430
477
  const persistentSandboxes = /* @__PURE__ */ new Map();
431
478
  const persistentSandboxCreating = /* @__PURE__ */ new Set();
479
+ const persistentSandboxCreationError = /* @__PURE__ */ new Map();
432
480
  const lazyCreatorAgent = /* @__PURE__ */ new Map();
481
+ const snapshotBaseCreatorAgent = /* @__PURE__ */ new Map();
433
482
  const threadSnapshots = /* @__PURE__ */ new Map();
434
483
  const persistentBaseSnapshot = /* @__PURE__ */ new Map();
435
484
  const persistentBaseSnapshotCreating = /* @__PURE__ */ new Set();
436
- workflow.setHandler(childSandboxReadySignal, ({ childWorkflowId, sandboxId }) => {
437
- const agentName = lazyCreatorAgent.get(childWorkflowId);
438
- if (agentName && !persistentSandboxes.has(agentName)) {
439
- persistentSandboxes.set(agentName, sandboxId);
440
- lazyCreatorAgent.delete(childWorkflowId);
485
+ const persistentBaseSnapshotCreationError = /* @__PURE__ */ new Map();
486
+ workflow.setHandler(
487
+ childSandboxReadySignal,
488
+ ({ childWorkflowId, sandboxId, baseSnapshot }) => {
489
+ const lazyAgent = lazyCreatorAgent.get(childWorkflowId);
490
+ if (lazyAgent && !persistentSandboxes.has(lazyAgent)) {
491
+ persistentSandboxes.set(lazyAgent, sandboxId);
492
+ lazyCreatorAgent.delete(childWorkflowId);
493
+ }
494
+ const snapAgent = snapshotBaseCreatorAgent.get(childWorkflowId);
495
+ if (snapAgent && baseSnapshot && !persistentBaseSnapshot.has(snapAgent)) {
496
+ persistentBaseSnapshot.set(snapAgent, baseSnapshot);
497
+ snapshotBaseCreatorAgent.delete(childWorkflowId);
498
+ }
441
499
  }
442
- });
500
+ );
443
501
  const handler = async (args, context) => {
444
502
  const config = subagents.find((s) => s.agentName === args.subagent);
445
503
  if (!config) {
@@ -495,8 +553,20 @@ function createSubagentHandler(subagents) {
495
553
  baseSnap = persistentBaseSnapshot.get(config.agentName);
496
554
  if (!baseSnap) {
497
555
  if (persistentBaseSnapshotCreating.has(config.agentName)) {
498
- await workflow.condition(() => persistentBaseSnapshot.has(config.agentName));
556
+ await workflow.condition(
557
+ () => persistentBaseSnapshot.has(config.agentName) || persistentBaseSnapshotCreationError.has(config.agentName) || !persistentBaseSnapshotCreating.has(config.agentName)
558
+ );
559
+ const creatorErr = persistentBaseSnapshotCreationError.get(
560
+ config.agentName
561
+ );
562
+ if (creatorErr !== void 0) {
563
+ throw creatorErr;
564
+ }
499
565
  baseSnap = persistentBaseSnapshot.get(config.agentName);
566
+ if (!baseSnap) {
567
+ persistentBaseSnapshotCreating.add(config.agentName);
568
+ isSnapshotBaseCreator = true;
569
+ }
500
570
  } else {
501
571
  persistentBaseSnapshotCreating.add(config.agentName);
502
572
  isSnapshotBaseCreator = true;
@@ -514,8 +584,20 @@ function createSubagentHandler(subagents) {
514
584
  baseSandboxId = persistentSandboxes.get(config.agentName);
515
585
  if (!baseSandboxId) {
516
586
  if (persistentSandboxCreating.has(config.agentName)) {
517
- await workflow.condition(() => persistentSandboxes.has(config.agentName));
587
+ await workflow.condition(
588
+ () => persistentSandboxes.has(config.agentName) || persistentSandboxCreationError.has(config.agentName) || !persistentSandboxCreating.has(config.agentName)
589
+ );
590
+ const creatorErr = persistentSandboxCreationError.get(
591
+ config.agentName
592
+ );
593
+ if (creatorErr !== void 0) {
594
+ throw creatorErr;
595
+ }
518
596
  baseSandboxId = persistentSandboxes.get(config.agentName);
597
+ if (!baseSandboxId) {
598
+ persistentSandboxCreating.add(config.agentName);
599
+ isLazyCreator = true;
600
+ }
519
601
  } else {
520
602
  persistentSandboxCreating.add(config.agentName);
521
603
  isLazyCreator = true;
@@ -544,6 +626,12 @@ function createSubagentHandler(subagents) {
544
626
  };
545
627
  const resolvedContext = config.context === void 0 ? void 0 : typeof config.context === "function" ? config.context() : config.context;
546
628
  const childOpts = {
629
+ // Apply a bounded run timeout by default so a child workflow that
630
+ // fails to initialize or otherwise never reaches a terminal state
631
+ // cannot hang the parent's `Subagent` tool call forever. Callers can
632
+ // raise, lower, or disable it via `workflowOptions.workflowRunTimeout`.
633
+ workflowRunTimeout: DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT,
634
+ ...config.workflowOptions ?? {},
547
635
  workflowId: childWorkflowId,
548
636
  args: resolvedContext === void 0 ? [args.prompt, workflowInput] : [args.prompt, workflowInput, resolvedContext],
549
637
  taskQueue: config.taskQueue ?? parentTaskQueue
@@ -551,13 +639,39 @@ function createSubagentHandler(subagents) {
551
639
  if (isLazyCreator) {
552
640
  lazyCreatorAgent.set(childWorkflowId, config.agentName);
553
641
  }
642
+ if (isSnapshotBaseCreator) {
643
+ snapshotBaseCreatorAgent.set(childWorkflowId, config.agentName);
644
+ }
554
645
  workflow.log.info("subagent spawned", {
555
646
  subagent: config.agentName,
556
647
  childWorkflowId,
557
648
  threadMode,
558
649
  sandboxSource: sandboxCfg.source
559
650
  });
560
- const childResult = await workflow.executeChild(config.workflow, childOpts);
651
+ let childResult;
652
+ try {
653
+ childResult = await workflow.executeChild(
654
+ config.workflow,
655
+ childOpts
656
+ );
657
+ } catch (err) {
658
+ workflow.log.warn("subagent failed", {
659
+ subagent: config.agentName,
660
+ childWorkflowId,
661
+ error: err instanceof Error ? err.message : String(err)
662
+ });
663
+ if (isLazyCreator) {
664
+ persistentSandboxCreating.delete(config.agentName);
665
+ persistentSandboxCreationError.set(config.agentName, err);
666
+ lazyCreatorAgent.delete(childWorkflowId);
667
+ }
668
+ if (isSnapshotBaseCreator) {
669
+ persistentBaseSnapshotCreating.delete(config.agentName);
670
+ persistentBaseSnapshotCreationError.set(config.agentName, err);
671
+ snapshotBaseCreatorAgent.delete(childWorkflowId);
672
+ }
673
+ throw err;
674
+ }
561
675
  const effectiveShutdown = sandboxShutdownOverride ?? sandboxCfg.shutdown ?? "destroy";
562
676
  workflow.log.info("subagent completed", {
563
677
  subagent: config.agentName,
@@ -601,10 +715,13 @@ function createSubagentHandler(subagents) {
601
715
  }
602
716
  if (isLazyCreator) {
603
717
  persistentSandboxCreating.delete(config.agentName);
718
+ persistentSandboxCreationError.delete(config.agentName);
604
719
  lazyCreatorAgent.delete(childWorkflowId);
605
720
  }
606
721
  if (isSnapshotBaseCreator) {
607
722
  persistentBaseSnapshotCreating.delete(config.agentName);
723
+ persistentBaseSnapshotCreationError.delete(config.agentName);
724
+ snapshotBaseCreatorAgent.delete(childWorkflowId);
608
725
  }
609
726
  if (!toolResponse) {
610
727
  return {
@@ -847,6 +964,7 @@ async function createSession({
847
964
  sandbox: sandboxInit,
848
965
  sandboxShutdown = "destroy",
849
966
  onSandboxReady,
967
+ onSessionExit,
850
968
  virtualFs: virtualFsConfig,
851
969
  virtualFsOps
852
970
  }) {
@@ -871,7 +989,9 @@ async function createSession({
871
989
  initializeThread,
872
990
  appendSystemMessage,
873
991
  appendAgentMessage,
874
- forkThread
992
+ forkThread,
993
+ loadThreadState,
994
+ saveThreadState
875
995
  } = threadOps;
876
996
  const plugins = [];
877
997
  let destroySubagentSandboxes;
@@ -965,8 +1085,10 @@ async function createSession({
965
1085
  nonRetryable: true
966
1086
  });
967
1087
  }
1088
+ const forkInit = sandboxInit;
968
1089
  sandboxId = await sandboxOps.forkSandbox(
969
- sandboxInit.sandboxId
1090
+ forkInit.sandboxId,
1091
+ forkInit.options
970
1092
  );
971
1093
  sandboxOwned = true;
972
1094
  } else if (sandboxMode === "from-snapshot") {
@@ -976,8 +1098,11 @@ async function createSession({
976
1098
  nonRetryable: true
977
1099
  });
978
1100
  }
979
- const snap = sandboxInit.snapshot;
980
- sandboxId = await sandboxOps.restoreSandbox(snap);
1101
+ const restoreInit = sandboxInit;
1102
+ sandboxId = await sandboxOps.restoreSandbox(
1103
+ restoreInit.snapshot,
1104
+ restoreInit.options
1105
+ );
981
1106
  sandboxOwned = true;
982
1107
  } else if (sandboxOps) {
983
1108
  const skillFiles = skills ? collectSkillFiles(skills) : void 0;
@@ -994,7 +1119,10 @@ async function createSession({
994
1119
  baseSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
995
1120
  }
996
1121
  if (sandboxId && sandboxOwned && onSandboxReady) {
997
- onSandboxReady(sandboxId);
1122
+ onSandboxReady({
1123
+ sandboxId,
1124
+ ...baseSnapshot && { baseSnapshot }
1125
+ });
998
1126
  }
999
1127
  if (virtualFsConfig) {
1000
1128
  if (!virtualFsOps) {
@@ -1037,9 +1165,20 @@ async function createSession({
1037
1165
  });
1038
1166
  const sessionStartMs = Date.now();
1039
1167
  const systemPrompt = stateManager.getSystemPrompt();
1168
+ const rehydrateFromSlice = (slice) => {
1169
+ stateManager.mergeUpdate({
1170
+ tasks: new Map(slice.tasks),
1171
+ ...slice.custom
1172
+ });
1173
+ };
1040
1174
  if (threadMode === "fork" && sourceThreadId) {
1041
1175
  await forkThread(sourceThreadId, threadId, threadKey);
1042
- } else if (threadMode === "continue") ; else {
1176
+ const forkedSlice = await loadThreadState(threadId, threadKey);
1177
+ if (forkedSlice) rehydrateFromSlice(forkedSlice);
1178
+ } else if (threadMode === "continue") {
1179
+ const continuedSlice = await loadThreadState(threadId, threadKey);
1180
+ if (continuedSlice) rehydrateFromSlice(continuedSlice);
1181
+ } else {
1043
1182
  if (appendSystemPrompt) {
1044
1183
  if (systemPrompt == null || typeof systemPrompt === "string" && systemPrompt.trim() === "") {
1045
1184
  throw workflow.ApplicationFailure.create({
@@ -1061,18 +1200,21 @@ async function createSession({
1061
1200
  let exitReason = "completed";
1062
1201
  let finalMessage = null;
1063
1202
  try {
1203
+ let assistantId;
1064
1204
  while (stateManager.isRunning() && !stateManager.isTerminal() && stateManager.getTurns() < maxTurns) {
1065
1205
  stateManager.incrementTurns();
1066
1206
  const currentTurn = stateManager.getTurns();
1067
1207
  workflow.log.debug("turn started", { agentName, threadId, turn: currentTurn });
1068
1208
  stateManager.setTools(toolRouter.getToolDefinitions());
1209
+ assistantId ??= workflow.uuid4();
1069
1210
  const { message, rawToolCalls, usage } = await runAgent({
1070
1211
  threadId,
1071
1212
  threadKey,
1072
1213
  agentName,
1073
- metadata
1214
+ metadata,
1215
+ assistantMessageId: assistantId
1074
1216
  });
1075
- await appendAgentMessage(threadId, workflow.uuid4(), message, threadKey);
1217
+ await appendAgentMessage(threadId, assistantId, message, threadKey);
1076
1218
  if (usage) {
1077
1219
  stateManager.updateUsage(usage);
1078
1220
  }
@@ -1117,6 +1259,18 @@ async function createSession({
1117
1259
  stateManager.updateUsage(result.usage);
1118
1260
  }
1119
1261
  }
1262
+ const rewind = toolCallResults.rewind;
1263
+ if (rewind) {
1264
+ workflow.log.info("rewinding turn", {
1265
+ agentName,
1266
+ threadId,
1267
+ turn: currentTurn,
1268
+ toolCallId: rewind.toolCallId,
1269
+ toolName: rewind.toolName
1270
+ });
1271
+ continue;
1272
+ }
1273
+ assistantId = void 0;
1120
1274
  if (stateManager.getStatus() === "WAITING_FOR_INPUT") {
1121
1275
  const conditionMet = await workflow.condition(
1122
1276
  () => stateManager.getStatus() === "RUNNING",
@@ -1149,6 +1303,19 @@ async function createSession({
1149
1303
  });
1150
1304
  throw workflow.ApplicationFailure.fromError(error);
1151
1305
  } finally {
1306
+ try {
1307
+ await saveThreadState(
1308
+ threadId,
1309
+ stateManager.getPersistedSlice(),
1310
+ threadKey
1311
+ );
1312
+ } catch (persistError) {
1313
+ workflow.log.warn("failed to persist thread state", {
1314
+ agentName,
1315
+ threadId,
1316
+ error: persistError instanceof Error ? persistError.message : String(persistError)
1317
+ });
1318
+ }
1152
1319
  await callSessionEnd(exitReason, stateManager.getTurns());
1153
1320
  if (sandboxOwned && sandboxId && sandboxOps) {
1154
1321
  switch (sandboxShutdown) {
@@ -1185,6 +1352,12 @@ async function createSession({
1185
1352
  ...baseSnapshot && { hasBaseSnapshot: true },
1186
1353
  ...exitSnapshot && { hasExitSnapshot: true }
1187
1354
  });
1355
+ if (onSessionExit) {
1356
+ onSessionExit({
1357
+ ...sandboxId && { sandboxId },
1358
+ ...exitSnapshot && { snapshot: exitSnapshot }
1359
+ });
1360
+ }
1188
1361
  return {
1189
1362
  threadId,
1190
1363
  finalMessage,
@@ -1213,6 +1386,18 @@ function defineWorkflow(config, fn) {
1213
1386
  return workflow;
1214
1387
  }
1215
1388
 
1389
+ // src/lib/thread/keys.ts
1390
+ var THREAD_TTL_SECONDS = 60 * 60 * 24 * 90;
1391
+ function getThreadListKey(threadKey, threadId) {
1392
+ return `${threadKey}:thread:${threadId}`;
1393
+ }
1394
+ function getThreadMetaKey(threadKey, threadId) {
1395
+ return `${threadKey}:meta:thread:${threadId}`;
1396
+ }
1397
+ function getThreadStateKey(threadKey, threadId) {
1398
+ return `${threadKey}:state:thread:${threadId}`;
1399
+ }
1400
+
1216
1401
  // src/lib/types.ts
1217
1402
  function isTerminalStatus(status) {
1218
1403
  return status === "COMPLETED" || status === "FAILED" || status === "CANCELLED";
@@ -1232,11 +1417,19 @@ function createAgentStateManager({
1232
1417
  let systemPrompt = initialState?.systemPrompt;
1233
1418
  const tasks = new Map(initialState?.tasks);
1234
1419
  const {
1235
- status: _,
1236
- version: __,
1237
- turns: ___,
1238
- tasks: ____,
1239
- tools: _____,
1420
+ status: _status,
1421
+ version: _version,
1422
+ turns: _turns,
1423
+ tasks: _tasks,
1424
+ tools: _tools,
1425
+ systemPrompt: _systemPrompt,
1426
+ fileTree: _fileTree,
1427
+ inlineFiles: _inlineFiles,
1428
+ virtualFsCtx: _virtualFsCtx,
1429
+ totalInputTokens: _totalInputTokens,
1430
+ totalOutputTokens: _totalOutputTokens,
1431
+ cachedWriteTokens: _cachedWriteTokens,
1432
+ cachedReadTokens: _cachedReadTokens,
1240
1433
  ...custom
1241
1434
  } = initialState ?? {};
1242
1435
  const customState = custom;
@@ -1316,7 +1509,14 @@ function createAgentStateManager({
1316
1509
  version++;
1317
1510
  },
1318
1511
  mergeUpdate(update) {
1319
- Object.assign(customState, update);
1512
+ const { tasks: nextTasks, ...rest } = update;
1513
+ if (nextTasks) {
1514
+ tasks.clear();
1515
+ for (const [id, task] of nextTasks) {
1516
+ tasks.set(id, task);
1517
+ }
1518
+ }
1519
+ Object.assign(customState, rest);
1320
1520
  version++;
1321
1521
  },
1322
1522
  getCurrentState() {
@@ -1354,6 +1554,12 @@ function createAgentStateManager({
1354
1554
  }
1355
1555
  return deleted;
1356
1556
  },
1557
+ getPersistedSlice() {
1558
+ return {
1559
+ tasks: Array.from(tasks.entries()),
1560
+ custom: { ...customState }
1561
+ };
1562
+ },
1357
1563
  updateUsage(usage) {
1358
1564
  totalInputTokens += usage.inputTokens ?? 0;
1359
1565
  totalOutputTokens += usage.outputTokens ?? 0;
@@ -1475,22 +1681,42 @@ function defineSubagentWorkflow(config, fn) {
1475
1681
  });
1476
1682
  }
1477
1683
  const parentHandle = workflow.getExternalWorkflowHandle(parent.workflowId);
1684
+ let capturedSandboxId;
1685
+ let capturedSnapshot;
1686
+ let capturedBaseSnapshot;
1687
+ let capturedThreadId;
1478
1688
  const sessionInput = {
1479
1689
  agentName: config.name,
1480
1690
  sandboxShutdown: effectiveShutdown,
1481
1691
  ...workflowInput.thread && { thread: workflowInput.thread },
1482
1692
  ...workflowInput.sandbox && { sandbox: workflowInput.sandbox },
1483
- onSandboxReady: (sandboxId) => {
1693
+ onSandboxReady: ({ sandboxId, baseSnapshot }) => {
1694
+ capturedBaseSnapshot = baseSnapshot;
1484
1695
  const isReuse = workflowInput.sandbox?.mode === "continue";
1485
1696
  if (!isReuse) {
1486
1697
  void parentHandle.signal(childSandboxReadySignal, {
1487
1698
  childWorkflowId: workflow.workflowInfo().workflowId,
1488
- sandboxId
1699
+ sandboxId,
1700
+ ...baseSnapshot && { baseSnapshot }
1489
1701
  });
1490
1702
  }
1703
+ },
1704
+ onSessionExit: ({ sandboxId, snapshot, threadId }) => {
1705
+ capturedSandboxId = sandboxId;
1706
+ capturedSnapshot = snapshot;
1707
+ capturedThreadId = threadId;
1708
+ }
1709
+ };
1710
+ const result = await fn(prompt, sessionInput, context ?? {});
1711
+ return {
1712
+ ...result,
1713
+ ...capturedThreadId !== void 0 && { threadId: capturedThreadId },
1714
+ ...capturedSandboxId !== void 0 && { sandboxId: capturedSandboxId },
1715
+ ...capturedSnapshot !== void 0 && { snapshot: capturedSnapshot },
1716
+ ...capturedBaseSnapshot !== void 0 && {
1717
+ baseSnapshot: capturedBaseSnapshot
1491
1718
  }
1492
1719
  };
1493
- return fn(prompt, sessionInput, context ?? {});
1494
1720
  };
1495
1721
  Object.defineProperty(workflow$1, "name", { value: config.name });
1496
1722
  return Object.assign(workflow$1, {
@@ -2260,7 +2486,6 @@ var FileSystemSkillProvider = class {
2260
2486
  };
2261
2487
 
2262
2488
  // src/lib/thread/manager.ts
2263
- var THREAD_TTL_SECONDS = 60 * 60 * 24 * 90;
2264
2489
  var APPEND_IDEMPOTENT_SCRIPT = `
2265
2490
  if redis.call('EXISTS', KEYS[1]) == 1 then
2266
2491
  return 0
@@ -2272,8 +2497,8 @@ redis.call('EXPIRE', KEYS[2], tonumber(ARGV[1]))
2272
2497
  redis.call('SET', KEYS[1], '1', 'EX', tonumber(ARGV[1]))
2273
2498
  return 1
2274
2499
  `;
2275
- function getThreadKey(threadId, key) {
2276
- return `${key}:thread:${threadId}`;
2500
+ function getDedupKey(threadId, id) {
2501
+ return `dedup:${id}:thread:${threadId}`;
2277
2502
  }
2278
2503
  function createThreadManager(config) {
2279
2504
  const {
@@ -2284,8 +2509,9 @@ function createThreadManager(config) {
2284
2509
  deserialize = (raw) => JSON.parse(raw),
2285
2510
  idOf
2286
2511
  } = config;
2287
- const redisKey = getThreadKey(threadId, key);
2288
- const metaKey = getThreadKey(threadId, `${key}:meta`);
2512
+ const redisKey = getThreadListKey(key, threadId);
2513
+ const metaKey = getThreadMetaKey(key, threadId);
2514
+ const stateKey = getThreadStateKey(key, threadId);
2289
2515
  async function assertThreadExists() {
2290
2516
  const exists = await redis.exists(metaKey);
2291
2517
  if (!exists) {
@@ -2307,7 +2533,7 @@ function createThreadManager(config) {
2307
2533
  await assertThreadExists();
2308
2534
  if (idOf) {
2309
2535
  const dedupId = messages.map(idOf).join(":");
2310
- const dedupKey = getThreadKey(threadId, `dedup:${dedupId}`);
2536
+ const dedupKey = getDedupKey(threadId, dedupId);
2311
2537
  await redis.eval(
2312
2538
  APPEND_IDEMPOTENT_SCRIPT,
2313
2539
  2,
@@ -2324,20 +2550,98 @@ function createThreadManager(config) {
2324
2550
  async fork(newThreadId) {
2325
2551
  await assertThreadExists();
2326
2552
  const data = await redis.lrange(redisKey, 0, -1);
2553
+ const stateRaw = await redis.get(stateKey);
2327
2554
  const forked = createThreadManager({
2328
2555
  ...config,
2329
2556
  threadId: newThreadId
2330
2557
  });
2331
2558
  await forked.initialize();
2332
2559
  if (data.length > 0) {
2333
- const newKey = getThreadKey(newThreadId, key);
2560
+ const newKey = getThreadListKey(key, newThreadId);
2334
2561
  await redis.rpush(newKey, ...data);
2335
2562
  await redis.expire(newKey, THREAD_TTL_SECONDS);
2336
2563
  }
2564
+ if (stateRaw != null) {
2565
+ const newStateKey = getThreadStateKey(key, newThreadId);
2566
+ await redis.set(newStateKey, stateRaw, "EX", THREAD_TTL_SECONDS);
2567
+ }
2337
2568
  return forked;
2338
2569
  },
2570
+ async replaceAll(messages) {
2571
+ await assertThreadExists();
2572
+ if (!idOf) {
2573
+ throw new Error(
2574
+ "replaceAll requires the thread manager to be configured with `idOf`"
2575
+ );
2576
+ }
2577
+ const existing = await redis.lrange(redisKey, 0, -1);
2578
+ const existingIds = existing.map((raw) => idOf(deserialize(raw))).filter((id) => typeof id === "string");
2579
+ await redis.del(redisKey);
2580
+ if (existingIds.length > 0) {
2581
+ await redis.del(
2582
+ ...existingIds.map((id) => getDedupKey(threadId, id))
2583
+ );
2584
+ }
2585
+ if (messages.length > 0) {
2586
+ await redis.rpush(redisKey, ...messages.map(serialize));
2587
+ await redis.expire(redisKey, THREAD_TTL_SECONDS);
2588
+ }
2589
+ await redis.expire(metaKey, THREAD_TTL_SECONDS);
2590
+ },
2339
2591
  async delete() {
2340
- await redis.del(redisKey, metaKey);
2592
+ await redis.del(redisKey, metaKey, stateKey);
2593
+ },
2594
+ async loadState() {
2595
+ const raw = await redis.get(stateKey);
2596
+ if (raw == null) return null;
2597
+ return JSON.parse(raw);
2598
+ },
2599
+ async saveState(state) {
2600
+ await assertThreadExists();
2601
+ await redis.set(
2602
+ stateKey,
2603
+ JSON.stringify(state),
2604
+ "EX",
2605
+ THREAD_TTL_SECONDS
2606
+ );
2607
+ },
2608
+ async deleteState() {
2609
+ await redis.del(stateKey);
2610
+ },
2611
+ async length() {
2612
+ await assertThreadExists();
2613
+ return redis.llen(redisKey);
2614
+ },
2615
+ async truncateFromId(messageId) {
2616
+ await assertThreadExists();
2617
+ if (!idOf) {
2618
+ throw new Error(
2619
+ "truncateFromId requires the thread manager to be configured with `idOf`"
2620
+ );
2621
+ }
2622
+ const data = await redis.lrange(redisKey, 0, -1);
2623
+ let idx = -1;
2624
+ const removedIds = [];
2625
+ for (let i = 0; i < data.length; i++) {
2626
+ const raw = data[i];
2627
+ if (raw === void 0) continue;
2628
+ const id = idOf(deserialize(raw));
2629
+ if (idx === -1 && id === messageId) idx = i;
2630
+ if (idx !== -1) removedIds.push(id);
2631
+ }
2632
+ if (idx === -1) return;
2633
+ if (idx === 0) {
2634
+ await redis.del(redisKey);
2635
+ await redis.expire(metaKey, THREAD_TTL_SECONDS);
2636
+ } else {
2637
+ await redis.ltrim(redisKey, 0, idx - 1);
2638
+ await redis.expire(redisKey, THREAD_TTL_SECONDS);
2639
+ }
2640
+ if (removedIds.length > 0) {
2641
+ await redis.del(
2642
+ ...removedIds.map((id) => getDedupKey(threadId, id))
2643
+ );
2644
+ }
2341
2645
  }
2342
2646
  };
2343
2647
  }
@@ -2424,18 +2728,18 @@ var SandboxManager = class {
2424
2728
  async resume(id) {
2425
2729
  await this.provider.resume(id);
2426
2730
  }
2427
- async snapshot(id) {
2428
- return this.provider.snapshot(id);
2731
+ async snapshot(id, options) {
2732
+ return this.provider.snapshot(id, options);
2429
2733
  }
2430
- async restore(snapshot) {
2431
- const sandbox = await this.provider.restore(snapshot);
2734
+ async restore(snapshot, options) {
2735
+ const sandbox = await this.provider.restore(snapshot, options);
2432
2736
  return sandbox.id;
2433
2737
  }
2434
2738
  async deleteSnapshot(snapshot) {
2435
2739
  await this.provider.deleteSnapshot(snapshot);
2436
2740
  }
2437
- async fork(sandboxId) {
2438
- const sandbox = await this.provider.fork(sandboxId);
2741
+ async fork(sandboxId, options) {
2742
+ const sandbox = await this.provider.fork(sandboxId, options);
2439
2743
  return sandbox.id;
2440
2744
  }
2441
2745
  /**
@@ -2473,17 +2777,17 @@ var SandboxManager = class {
2473
2777
  resumeSandbox: async (sandboxId) => {
2474
2778
  await this.resume(sandboxId);
2475
2779
  },
2476
- snapshotSandbox: async (sandboxId) => {
2477
- return this.snapshot(sandboxId);
2780
+ snapshotSandbox: async (sandboxId, options) => {
2781
+ return this.snapshot(sandboxId, options);
2478
2782
  },
2479
- restoreSandbox: async (snapshot) => {
2480
- return this.restore(snapshot);
2783
+ restoreSandbox: async (snapshot, options) => {
2784
+ return this.restore(snapshot, options);
2481
2785
  },
2482
2786
  deleteSandboxSnapshot: async (snapshot) => {
2483
2787
  await this.deleteSnapshot(snapshot);
2484
2788
  },
2485
- forkSandbox: async (sandboxId) => {
2486
- return this.fork(sandboxId);
2789
+ forkSandbox: async (sandboxId, options) => {
2790
+ return this.fork(sandboxId, options);
2487
2791
  }
2488
2792
  };
2489
2793
  const cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
@@ -3152,11 +3456,13 @@ var toTree = async (fs, opts = {}) => {
3152
3456
  return base + subtree;
3153
3457
  };
3154
3458
 
3459
+ exports.DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT = DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT;
3155
3460
  exports.FileSystemSkillProvider = FileSystemSkillProvider;
3156
3461
  exports.NodeFsSandboxFileSystem = NodeFsSandboxFileSystem;
3157
3462
  exports.SandboxManager = SandboxManager;
3158
3463
  exports.SandboxNotFoundError = SandboxNotFoundError;
3159
3464
  exports.SandboxNotSupportedError = SandboxNotSupportedError;
3465
+ exports.THREAD_TTL_SECONDS = THREAD_TTL_SECONDS;
3160
3466
  exports.VirtualFileSystem = VirtualFileSystem;
3161
3467
  exports.applyVirtualTreeMutations = applyVirtualTreeMutations;
3162
3468
  exports.askUserQuestionTool = askUserQuestionTool;
@@ -3188,6 +3494,8 @@ exports.filesWithMimeType = filesWithMimeType;
3188
3494
  exports.formatVirtualFileTree = formatVirtualFileTree;
3189
3495
  exports.getActivityContext = getActivityContext;
3190
3496
  exports.getShortId = getShortId;
3497
+ exports.getThreadListKey = getThreadListKey;
3498
+ exports.getThreadMetaKey = getThreadMetaKey;
3191
3499
  exports.globHandler = globHandler;
3192
3500
  exports.globTool = globTool;
3193
3501
  exports.grepTool = grepTool;