zeitlich 0.2.36 → 0.2.37

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 (200) hide show
  1. package/README.md +146 -92
  2. package/dist/{activities-BVI2lTwr.d.ts → activities-Bb-nAjwQ.d.ts} +2 -2
  3. package/dist/{activities-hd4aNnZE.d.cts → activities-vkI4_3CC.d.cts} +2 -2
  4. package/dist/adapters/sandbox/bedrock/index.cjs +14 -11
  5. package/dist/adapters/sandbox/bedrock/index.cjs.map +1 -1
  6. package/dist/adapters/sandbox/bedrock/index.d.cts +4 -3
  7. package/dist/adapters/sandbox/bedrock/index.d.ts +4 -3
  8. package/dist/adapters/sandbox/bedrock/index.js +14 -11
  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 +8 -0
  17. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  18. package/dist/adapters/sandbox/daytona/index.d.cts +2 -1
  19. package/dist/adapters/sandbox/daytona/index.d.ts +2 -1
  20. package/dist/adapters/sandbox/daytona/index.js +8 -0
  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 +59 -10
  29. package/dist/adapters/sandbox/e2b/index.cjs.map +1 -1
  30. package/dist/adapters/sandbox/e2b/index.d.cts +5 -3
  31. package/dist/adapters/sandbox/e2b/index.d.ts +5 -3
  32. package/dist/adapters/sandbox/e2b/index.js +59 -10
  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 +5 -0
  41. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
  42. package/dist/adapters/sandbox/inmemory/index.d.cts +2 -1
  43. package/dist/adapters/sandbox/inmemory/index.d.ts +2 -1
  44. package/dist/adapters/sandbox/inmemory/index.js +5 -0
  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 +71 -36
  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 +71 -36
  57. package/dist/adapters/thread/anthropic/index.js.map +1 -1
  58. package/dist/adapters/thread/anthropic/workflow.cjs +5 -1
  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 +5 -1
  63. package/dist/adapters/thread/anthropic/workflow.js.map +1 -1
  64. package/dist/adapters/thread/google-genai/index.cjs +50 -25
  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 +50 -25
  69. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  70. package/dist/adapters/thread/google-genai/workflow.cjs +5 -1
  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 +5 -1
  75. package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
  76. package/dist/adapters/thread/langchain/index.cjs +34 -7
  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 +34 -7
  81. package/dist/adapters/thread/langchain/index.js.map +1 -1
  82. package/dist/adapters/thread/langchain/workflow.cjs +5 -1
  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 +5 -1
  87. package/dist/adapters/thread/langchain/workflow.js.map +1 -1
  88. package/dist/index.cjs +206 -120
  89. package/dist/index.cjs.map +1 -1
  90. package/dist/index.d.cts +17 -11
  91. package/dist/index.d.ts +17 -11
  92. package/dist/index.js +207 -121
  93. package/dist/index.js.map +1 -1
  94. package/dist/{proxy-BjdFGPTm.d.ts → proxy-0smGKvx8.d.ts} +1 -1
  95. package/dist/{proxy-7RnVaPdJ.d.cts → proxy-DEtowJyd.d.cts} +1 -1
  96. package/dist/{thread-manager-DjN5JYul.d.ts → thread-manager-3fszQih4.d.ts} +2 -2
  97. package/dist/{thread-manager-CbpiGq1L.d.ts → thread-manager-C-C4pI2z.d.ts} +2 -2
  98. package/dist/{thread-manager-BBzNgQWH.d.cts → thread-manager-CzYln2OC.d.cts} +2 -2
  99. package/dist/{thread-manager-DzXm9eeI.d.cts → thread-manager-D4vgzYrh.d.cts} +2 -2
  100. package/dist/{types-yiXmqedU.d.ts → types-B37hKoWA.d.ts} +1 -1
  101. package/dist/{types-DQ1l_gXL.d.cts → types-BO7Yju20.d.cts} +63 -14
  102. package/dist/{types-wiGLvxWf.d.ts → types-CNuWnvy9.d.ts} +1 -1
  103. package/dist/{types-CADc5V_P.d.ts → types-CPKDl-y_.d.ts} +63 -14
  104. package/dist/{types-Mc_4BCfT.d.cts → types-D08CXPh8.d.cts} +1 -1
  105. package/dist/{types-CBH54cwr.d.cts → types-DWEUmYAJ.d.cts} +1 -1
  106. package/dist/{types-DxCpFNv_.d.cts → types-tQL9njTu.d.cts} +25 -0
  107. package/dist/{types-DxCpFNv_.d.ts → types-tQL9njTu.d.ts} +25 -0
  108. package/dist/{workflow-P2pTSfKu.d.ts → workflow-CjXHbZZc.d.ts} +2 -2
  109. package/dist/{workflow-DhtWRovz.d.cts → workflow-Do_lzJpT.d.cts} +2 -2
  110. package/dist/workflow.cjs +182 -114
  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 +183 -115
  115. package/dist/workflow.js.map +1 -1
  116. package/package.json +1 -1
  117. package/src/adapters/sandbox/bedrock/filesystem.ts +6 -12
  118. package/src/adapters/sandbox/bedrock/index.ts +10 -8
  119. package/src/adapters/sandbox/bedrock/proxy.ts +2 -0
  120. package/src/adapters/sandbox/daytona/index.ts +6 -0
  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 +63 -12
  124. package/src/adapters/sandbox/e2b/proxy.ts +2 -0
  125. package/src/adapters/sandbox/inmemory/index.ts +5 -0
  126. package/src/adapters/sandbox/inmemory/proxy.ts +2 -0
  127. package/src/adapters/thread/anthropic/activities.ts +49 -26
  128. package/src/adapters/thread/anthropic/model-invoker.ts +15 -6
  129. package/src/adapters/thread/anthropic/proxy.ts +6 -2
  130. package/src/adapters/thread/anthropic/thread-manager.test.ts +26 -7
  131. package/src/adapters/thread/anthropic/thread-manager.ts +60 -46
  132. package/src/adapters/thread/google-genai/activities.ts +7 -2
  133. package/src/adapters/thread/google-genai/model-invoker.ts +26 -8
  134. package/src/adapters/thread/google-genai/proxy.ts +6 -2
  135. package/src/adapters/thread/google-genai/thread-manager.test.ts +13 -3
  136. package/src/adapters/thread/google-genai/thread-manager.ts +54 -33
  137. package/src/adapters/thread/langchain/activities.ts +46 -24
  138. package/src/adapters/thread/langchain/hooks.test.ts +36 -49
  139. package/src/adapters/thread/langchain/hooks.ts +18 -5
  140. package/src/adapters/thread/langchain/model-invoker.ts +3 -3
  141. package/src/adapters/thread/langchain/proxy.ts +6 -2
  142. package/src/adapters/thread/langchain/thread-manager.test.ts +5 -1
  143. package/src/adapters/thread/langchain/thread-manager.ts +20 -9
  144. package/src/index.ts +4 -1
  145. package/src/lib/activity.ts +16 -6
  146. package/src/lib/hooks/types.ts +6 -6
  147. package/src/lib/lifecycle.ts +9 -1
  148. package/src/lib/model/proxy.ts +2 -2
  149. package/src/lib/observability/hooks.ts +4 -5
  150. package/src/lib/observability/index.ts +1 -4
  151. package/src/lib/sandbox/manager.ts +21 -4
  152. package/src/lib/sandbox/node-fs.ts +3 -6
  153. package/src/lib/sandbox/sandbox.test.ts +36 -3
  154. package/src/lib/sandbox/tree.integration.test.ts +10 -3
  155. package/src/lib/sandbox/types.ts +35 -1
  156. package/src/lib/session/session-edge-cases.integration.test.ts +51 -13
  157. package/src/lib/session/session.integration.test.ts +139 -0
  158. package/src/lib/session/session.ts +50 -19
  159. package/src/lib/session/types.ts +13 -5
  160. package/src/lib/skills/fs-provider.ts +12 -8
  161. package/src/lib/skills/handler.ts +1 -1
  162. package/src/lib/skills/parse.ts +3 -1
  163. package/src/lib/skills/register.ts +1 -3
  164. package/src/lib/skills/skills.integration.test.ts +25 -15
  165. package/src/lib/state/manager.integration.test.ts +12 -2
  166. package/src/lib/subagent/define.ts +1 -1
  167. package/src/lib/subagent/handler.ts +186 -71
  168. package/src/lib/subagent/index.ts +1 -5
  169. package/src/lib/subagent/register.ts +3 -2
  170. package/src/lib/subagent/signals.ts +1 -10
  171. package/src/lib/subagent/subagent.integration.test.ts +438 -156
  172. package/src/lib/subagent/tool.ts +4 -3
  173. package/src/lib/subagent/types.ts +50 -20
  174. package/src/lib/subagent/workflow.ts +9 -49
  175. package/src/lib/thread/id.test.ts +1 -1
  176. package/src/lib/thread/id.ts +1 -2
  177. package/src/lib/thread/proxy.ts +3 -4
  178. package/src/lib/thread/types.ts +11 -3
  179. package/src/lib/tool-router/index.ts +1 -5
  180. package/src/lib/tool-router/router-edge-cases.integration.test.ts +1 -1
  181. package/src/lib/tool-router/router.ts +3 -2
  182. package/src/lib/tool-router/types.ts +11 -3
  183. package/src/lib/tool-router/with-sandbox.ts +19 -5
  184. package/src/lib/virtual-fs/filesystem.ts +1 -1
  185. package/src/lib/virtual-fs/index.ts +5 -1
  186. package/src/lib/virtual-fs/mutations.ts +2 -4
  187. package/src/lib/virtual-fs/queries.ts +9 -5
  188. package/src/lib/virtual-fs/types.ts +4 -1
  189. package/src/lib/virtual-fs/virtual-fs.test.ts +9 -11
  190. package/src/lib/workflow.test.ts +7 -4
  191. package/src/lib/workflow.ts +1 -5
  192. package/src/tools/ask-user-question/tool.ts +1 -3
  193. package/src/tools/glob/handler.ts +1 -4
  194. package/src/tools/task-get/handler.ts +4 -5
  195. package/src/tools/task-list/handler.ts +1 -4
  196. package/src/tools/task-update/handler.ts +4 -5
  197. package/src/workflow.ts +20 -7
  198. package/tsup.config.ts +9 -6
  199. package/src/lib/.env +0 -1
  200. package/src/tools/bash/.env +0 -1
package/README.md CHANGED
@@ -39,15 +39,16 @@ Zeitlich's core is framework-agnostic — it defines generic interfaces (`ModelI
39
39
  ### Thread Adapters
40
40
 
41
41
  A thread adapter bundles two concerns:
42
+
42
43
  1. **Thread management** — Storing and retrieving conversation messages in Redis
43
44
  2. **Model invocation** — Calling the LLM with the conversation history and tools
44
45
 
45
46
  Each adapter exposes the same shape: `createActivities(scope)` for Temporal worker registration, and an `invoker` for model calls. Pick the one matching your preferred SDK:
46
47
 
47
- | Adapter | Import | SDK |
48
- |---------|--------|-----|
49
- | LangChain | `zeitlich/adapters/thread/langchain` | `@langchain/core` + any provider package |
50
- | Google GenAI | `zeitlich/adapters/thread/google-genai` | `@google/genai` |
48
+ | Adapter | Import | SDK |
49
+ | ------------ | --------------------------------------- | ---------------------------------------- |
50
+ | LangChain | `zeitlich/adapters/thread/langchain` | `@langchain/core` + any provider package |
51
+ | Google GenAI | `zeitlich/adapters/thread/google-genai` | `@google/genai` |
51
52
 
52
53
  Vercel AI SDK and other provider-specific adapters can be built by implementing the `ThreadOps` and `ModelInvoker` interfaces.
53
54
 
@@ -55,13 +56,13 @@ Vercel AI SDK and other provider-specific adapters can be built by implementing
55
56
 
56
57
  A sandbox adapter provides filesystem access for tools like `Bash`, `Read`, `Write`, and `Edit`:
57
58
 
58
- | Adapter | Import | Use case |
59
- |---------|--------|----------|
60
- | In-memory | `zeitlich/adapters/sandbox/inmemory` | Tests and lightweight agents |
61
- | Virtual FS | `zeitlich` / `zeitlich/workflow` | Built-in virtual filesystem with custom resolvers |
62
- | Daytona | `zeitlich/adapters/sandbox/daytona` | Remote Daytona workspaces |
63
- | E2B | `zeitlich/adapters/sandbox/e2b` | E2B cloud sandboxes |
64
- | Bedrock | `zeitlich/adapters/sandbox/bedrock` | AWS Bedrock AgentCore Code Interpreter |
59
+ | Adapter | Import | Use case |
60
+ | ---------- | ------------------------------------ | ------------------------------------------------- |
61
+ | In-memory | `zeitlich/adapters/sandbox/inmemory` | Tests and lightweight agents |
62
+ | Virtual FS | `zeitlich` / `zeitlich/workflow` | Built-in virtual filesystem with custom resolvers |
63
+ | Daytona | `zeitlich/adapters/sandbox/daytona` | Remote Daytona workspaces |
64
+ | E2B | `zeitlich/adapters/sandbox/e2b` | E2B cloud sandboxes |
65
+ | Bedrock | `zeitlich/adapters/sandbox/bedrock` | AWS Bedrock AgentCore Code Interpreter |
65
66
 
66
67
  ### Example: LangChain Adapter
67
68
 
@@ -442,13 +443,15 @@ export const researcherWorkflow = defineSubagentWorkflow(
442
443
  buildContextMessage: () => [{ type: "text", text: prompt }],
443
444
  });
444
445
 
445
- const { finalMessage, threadId } = await session.runSession({ stateManager });
446
+ const { finalMessage, threadId } = await session.runSession({
447
+ stateManager,
448
+ });
446
449
  return {
447
450
  toolResponse: finalMessage ? extractText(finalMessage) : "No response",
448
451
  data: null,
449
452
  threadId,
450
453
  };
451
- },
454
+ }
452
455
  );
453
456
  ```
454
457
 
@@ -510,6 +513,7 @@ license: MIT
510
513
  ## Instructions
511
514
 
512
515
  When reviewing code, follow these steps:
516
+
513
517
  1. Read the diff with `Bash`
514
518
  2. Search for related tests with `Grep`
515
519
  3. Read the checklist from `resources/checklist.md`
@@ -591,7 +595,7 @@ For advanced use cases, you can construct the tool and handler independently:
591
595
  ```typescript
592
596
  import { createReadSkillTool, createReadSkillHandler } from "zeitlich/workflow";
593
597
 
594
- const tool = createReadSkillTool(skills); // ToolDefinition with enum schema
598
+ const tool = createReadSkillTool(skills); // ToolDefinition with enum schema
595
599
  const handler = createReadSkillHandler(skills); // Returns skill instructions
596
600
  ```
597
601
 
@@ -603,11 +607,11 @@ Every session has a **thread** (conversation history) and an optional **sandbox*
603
607
 
604
608
  The `thread` field on `SessionConfig` (and `WorkflowInput`) accepts one of three modes:
605
609
 
606
- | Mode | Description |
607
- |------|-------------|
608
- | `{ mode: "new" }` | Start a fresh thread (default). Optionally pass `threadId` to choose the ID. |
609
- | `{ mode: "fork", threadId }` | Copy all messages from an existing thread into a new one and continue there. The original is never mutated. |
610
- | `{ mode: "continue", threadId }` | Append directly to an existing thread in-place. |
610
+ | Mode | Description |
611
+ | -------------------------------- | ----------------------------------------------------------------------------------------------------------- |
612
+ | `{ mode: "new" }` | Start a fresh thread (default). Optionally pass `threadId` to choose the ID. |
613
+ | `{ mode: "fork", threadId }` | Copy all messages from an existing thread into a new one and continue there. The original is never mutated. |
614
+ | `{ mode: "continue", threadId }` | Append directly to an existing thread in-place. |
611
615
 
612
616
  ```typescript
613
617
  import { createSession } from "zeitlich/workflow";
@@ -637,22 +641,24 @@ const continuedSession = await createSession({
637
641
 
638
642
  The `sandbox` field controls how a sandbox is created or reused:
639
643
 
640
- | Mode | Description |
641
- |------|-------------|
642
- | `{ mode: "new" }` | Create a fresh sandbox (default when `sandboxOps` is provided). |
643
- | `{ mode: "continue", sandboxId }` | Take ownership of an existing sandbox (paused or running). Paused sandboxes are automatically resumed. |
644
- | `{ mode: "fork", sandboxId }` | Fork from an existing sandbox. A new sandbox is created and owned by this session. |
645
- | `{ mode: "inherit", sandboxId }` | Use a sandbox owned by someone else (e.g. a parent agent). Shutdown policy is ignored. |
644
+ | Mode | Description |
645
+ | ------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
646
+ | `{ mode: "new" }` | Create a fresh sandbox (default when `sandboxOps` is provided). |
647
+ | `{ mode: "continue", sandboxId }` | Take ownership of an existing sandbox (paused or running). Paused sandboxes are automatically resumed. |
648
+ | `{ mode: "fork", sandboxId }` | Fork from an existing sandbox. A new sandbox is created and owned by this session. |
649
+ | `{ mode: "from-snapshot", snapshot }` | Restore a fresh sandbox from a previously captured `SandboxSnapshot`. The new sandbox is owned by this session. |
650
+ | `{ mode: "inherit", sandboxId }` | Use a sandbox owned by someone else (e.g. a parent agent). Shutdown policy is ignored. |
646
651
 
647
652
  #### Sandbox Shutdown (`SandboxShutdown`)
648
653
 
649
654
  The `sandboxShutdown` field controls what happens to the sandbox when the session exits:
650
655
 
651
- | Value | Description |
652
- |-------|-------------|
653
- | `"destroy"` | Tear down the sandbox entirely (default). |
654
- | `"pause"` | Pause the sandbox so it can be resumed later. |
655
- | `"keep"` | Leave the sandbox running (no-op on exit). |
656
+ | Value | Description |
657
+ | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
658
+ | `"destroy"` | Tear down the sandbox entirely (default). |
659
+ | `"pause"` | Pause the sandbox so it can be resumed later. |
660
+ | `"keep"` | Leave the sandbox running (no-op on exit). |
661
+ | `"snapshot"` | Capture a snapshot, then destroy the sandbox. The snapshot is surfaced on the session result as `snapshot` (plus `baseSnapshot` when the sandbox was freshly created). |
656
662
 
657
663
  Subagents also support two additional shutdown modes:
658
664
 
@@ -692,6 +698,47 @@ export const codeRunnerSubagent = defineSubagent(codeRunnerWorkflow, {
692
698
  });
693
699
  ```
694
700
 
701
+ ##### Snapshot-driven continuation (E2B only)
702
+
703
+ `continuation: "snapshot"` avoids keeping a sandbox paused between invocations. Instead, each call boots a fresh sandbox from a stored snapshot, captures a new snapshot on exit, and destroys the sandbox inline:
704
+
705
+ ```typescript
706
+ export const analystSubagent = defineSubagent(analystWorkflow, {
707
+ thread: "continue",
708
+ sandbox: { source: "own", init: "once", continuation: "snapshot" },
709
+ });
710
+ ```
711
+
712
+ How it works:
713
+
714
+ - **First call** for a thread: session creates a fresh sandbox, snapshots it right after seeding (the **base snapshot**, kept per-agent with `init: "once"`), runs the agent, snapshots again on exit (the **thread snapshot**), and destroys the sandbox.
715
+ - **Same-thread follow-up**: session restores the thread's latest snapshot into a new sandbox, runs, snapshots on exit, destroys. The superseded snapshot is deleted eagerly.
716
+ - **New thread, same agent** (`init: "once"`): session restores from the base snapshot, skipping re-seeding.
717
+ - `thread: "fork"` preserves the source thread's snapshot (the fork writes to a new key) so the source can still be continued later.
718
+
719
+ Snapshots are cleaned up by the **child workflow that produced them**, not by the parent. Each snapshot-producing subagent stays alive after signalling its result back to the parent, and waits for a `cleanupSnapshots` signal. When the parent session exits, it fans that signal out to every pending snapshot-owner child; each child deletes its own snapshots via its own `sandboxOps` and terminates.
720
+
721
+ This means heterogeneous providers "just work" — the parent doesn't need to know (or even have) `sandboxOps` for the provider the child used. The child wraps its `deleteSnapshots` callback via `session.runSession()` and the workflow wrapper does the rest:
722
+
723
+ ```typescript
724
+ export const analystWorkflow = defineSubagentWorkflow(
725
+ { name: "analyst", description: "...", sandboxShutdown: "snapshot" },
726
+ async (prompt, sessionInput) => {
727
+ const session = await createSession({
728
+ ...sessionInput,
729
+ sandboxOps,
730
+ runAgent,
731
+ threadOps,
732
+ buildContextMessage: () => prompt,
733
+ });
734
+ const result = await session.runSession({ stateManager });
735
+ return result; // result.deleteSnapshots is forwarded automatically
736
+ }
737
+ );
738
+ ```
739
+
740
+ Trade-off: cleanup is deferred to parent close (no eager GC of superseded thread snapshots). Extra cost is a few snapshot IDs held for the parent's lifetime — much cheaper than keeping sandboxes paused. Currently implemented for the E2B adapter. The in-memory adapter treats snapshots as opaque caller-held data (delete is a no-op), and Daytona/Bedrock throw `SandboxNotSupportedError` for snapshot operations.
741
+
695
742
  The `thread` field accepts `"new"` (default), `"fork"`, or `"continue"`. When set to `"fork"` or `"continue"`, the parent agent can pass a `threadId` in a subsequent `Task` tool call to resume the conversation. The subagent returns its `threadId` in the response (surfaced as `[Thread ID: ...]`), which the parent can use for continuation.
696
743
 
697
744
  The `sandbox` field accepts `"none"` (default) or an object with `source`, `continuation`, and optional `init`/`shutdown` fields:
@@ -714,9 +761,11 @@ export const researcherWorkflow = defineSubagentWorkflow(
714
761
  // ... other config
715
762
  });
716
763
 
717
- const { threadId, finalMessage } = await session.runSession({ stateManager });
764
+ const { threadId, finalMessage } = await session.runSession({
765
+ stateManager,
766
+ });
718
767
  return { toolResponse: extractText(finalMessage), data: null, threadId };
719
- },
768
+ }
720
769
  );
721
770
  ```
722
771
 
@@ -790,10 +839,15 @@ Each `fs` instance also exposes `workspaceBase`, which is the base used for rela
790
839
  ```typescript
791
840
  import { VirtualFileSystem } from "zeitlich";
792
841
 
793
- const virtualFs = new VirtualFileSystem(fileTree, resolver, { projectId: "p1" }, "/repo");
842
+ const virtualFs = new VirtualFileSystem(
843
+ fileTree,
844
+ resolver,
845
+ { projectId: "p1" },
846
+ "/repo"
847
+ );
794
848
  console.log(virtualFs.workspaceBase); // "/repo"
795
849
 
796
- await virtualFs.writeFile("src/index.ts", 'export const ok = true;\n');
850
+ await virtualFs.writeFile("src/index.ts", "export const ok = true;\n");
797
851
  const content = await virtualFs.readFile("src/index.ts"); // reads /repo/src/index.ts
798
852
  ```
799
853
 
@@ -879,80 +933,80 @@ const session = await createSession({
879
933
 
880
934
  Safe for use in Temporal workflow files:
881
935
 
882
- | Export | Description |
883
- | --------------------------- | ------------------------------------------------------------------------------------------------------ |
884
- | `createSession` | Creates an agent session with tools, prompts, subagents, and hooks |
885
- | `createAgentStateManager` | Creates a state manager for workflow state with query/update handlers |
886
- | `createToolRouter` | Creates a tool router (used internally by session, or for advanced use) |
887
- | `defineTool` | Identity function for type-safe tool definition with handler and hooks |
888
- | `defineSubagentWorkflow` | Defines a subagent workflow with embedded name, description, and optional resultSchema |
889
- | `defineSubagent` | Creates a `SubagentConfig` from a `SubagentDefinition` with optional parent-specific overrides |
890
- | `proxyRunAgent` | Workflow-safe proxy for `runAgent` activities with LLM-optimised defaults (heartbeat, timeouts, retries) |
891
- | `getShortId` | Generate a compact, workflow-deterministic identifier (base-62, 12 chars) |
892
- | Tool definitions | `askUserQuestionTool`, `globTool`, `grepTool`, `readFileTool`, `writeFileTool`, `editTool`, `bashTool` |
893
- | Task tools | `taskCreateTool`, `taskGetTool`, `taskListTool`, `taskUpdateTool` for workflow task management |
894
- | Skill utilities | `parseSkillFile`, `createReadSkillTool`, `createReadSkillHandler` |
895
- | `defineWorkflow` | Wraps a main workflow function, translating `WorkflowInput` into session-compatible fields |
896
- | Lifecycle types | `ThreadInit`, `SandboxInit`, `SandboxShutdown`, `SubagentSandboxShutdown`, `SubagentSandboxConfig` |
897
- | Types | `Skill`, `SkillMetadata`, `SkillProvider`, `SubagentDefinition`, `SubagentConfig`, `ToolDefinition`, `ToolWithHandler`, `RouterContext`, `SessionConfig`, `WorkflowConfig`, `WorkflowInput`, etc. |
936
+ | Export | Description |
937
+ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
938
+ | `createSession` | Creates an agent session with tools, prompts, subagents, and hooks |
939
+ | `createAgentStateManager` | Creates a state manager for workflow state with query/update handlers |
940
+ | `createToolRouter` | Creates a tool router (used internally by session, or for advanced use) |
941
+ | `defineTool` | Identity function for type-safe tool definition with handler and hooks |
942
+ | `defineSubagentWorkflow` | Defines a subagent workflow with embedded name, description, and optional resultSchema |
943
+ | `defineSubagent` | Creates a `SubagentConfig` from a `SubagentDefinition` with optional parent-specific overrides |
944
+ | `proxyRunAgent` | Workflow-safe proxy for `runAgent` activities with LLM-optimised defaults (heartbeat, timeouts, retries) |
945
+ | `getShortId` | Generate a compact, workflow-deterministic identifier (base-62, 12 chars) |
946
+ | Tool definitions | `askUserQuestionTool`, `globTool`, `grepTool`, `readFileTool`, `writeFileTool`, `editTool`, `bashTool` |
947
+ | Task tools | `taskCreateTool`, `taskGetTool`, `taskListTool`, `taskUpdateTool` for workflow task management |
948
+ | Skill utilities | `parseSkillFile`, `createReadSkillTool`, `createReadSkillHandler` |
949
+ | `defineWorkflow` | Wraps a main workflow function, translating `WorkflowInput` into session-compatible fields |
950
+ | Lifecycle types | `ThreadInit`, `SandboxInit`, `SandboxShutdown`, `SubagentSandboxShutdown`, `SubagentSandboxConfig` |
951
+ | Types | `Skill`, `SkillMetadata`, `SkillProvider`, `SubagentDefinition`, `SubagentConfig`, `ToolDefinition`, `ToolWithHandler`, `RouterContext`, `SessionConfig`, `WorkflowConfig`, `WorkflowInput`, etc. |
898
952
 
899
953
  ### Activity Entry Point (`zeitlich`)
900
954
 
901
955
  Framework-agnostic utilities for activities, worker setup, and Node.js code:
902
956
 
903
- | Export | Description |
904
- | ------------------------- | --------------------------------------------------------------------------------------------- |
905
- | `createRunAgentActivity` | Wraps a handler into a scope-prefixed `RunAgentActivity` with auto-fetched parent workflow state |
906
- | `withParentWorkflowState` | Wraps a tool handler into an `ActivityToolHandler` with auto-fetched parent workflow state |
907
- | `createThreadManager` | Generic Redis-backed thread manager factory |
908
- | `toTree` | Generate file tree string from an `IFileSystem` instance |
909
- | `withSandbox` | Wraps a handler to auto-resolve sandbox from context (pairs with `withAutoAppend`) |
910
- | `NodeFsSandboxFileSystem` | `node:fs` adapter for `SandboxFileSystem` — read skills from the worker's local disk |
911
- | `FileSystemSkillProvider` | Load skills from a directory following the agentskills.io layout |
957
+ | Export | Description |
958
+ | ------------------------- | ------------------------------------------------------------------------------------------------------------------ |
959
+ | `createRunAgentActivity` | Wraps a handler into a scope-prefixed `RunAgentActivity` with auto-fetched parent workflow state |
960
+ | `withParentWorkflowState` | Wraps a tool handler into an `ActivityToolHandler` with auto-fetched parent workflow state |
961
+ | `createThreadManager` | Generic Redis-backed thread manager factory |
962
+ | `toTree` | Generate file tree string from an `IFileSystem` instance |
963
+ | `withSandbox` | Wraps a handler to auto-resolve sandbox from context (pairs with `withAutoAppend`) |
964
+ | `NodeFsSandboxFileSystem` | `node:fs` adapter for `SandboxFileSystem` — read skills from the worker's local disk |
965
+ | `FileSystemSkillProvider` | Load skills from a directory following the agentskills.io layout |
912
966
  | Tool handlers | `bashHandler`, `editHandler`, `globHandler`, `readFileHandler`, `writeFileHandler`, `createAskUserQuestionHandler` |
913
967
 
914
968
  ### Thread Adapter Entry Points
915
969
 
916
970
  **LangChain** (`zeitlich/adapters/thread/langchain`):
917
971
 
918
- | Export | Description |
919
- | ----------------------------------- | ---------------------------------------------------------------------- |
920
- | `createLangChainAdapter` | Unified adapter returning `createActivities`, `invoker`, `createModelInvoker` |
921
- | `createLangChainModelInvoker` | Factory that returns a `ModelInvoker` backed by a LangChain chat model |
922
- | `invokeLangChainModel` | One-shot model invocation convenience function |
923
- | `createLangChainThreadManager` | Thread manager with LangChain `StoredMessage` helpers |
972
+ | Export | Description |
973
+ | ------------------------------ | ----------------------------------------------------------------------------- |
974
+ | `createLangChainAdapter` | Unified adapter returning `createActivities`, `invoker`, `createModelInvoker` |
975
+ | `createLangChainModelInvoker` | Factory that returns a `ModelInvoker` backed by a LangChain chat model |
976
+ | `invokeLangChainModel` | One-shot model invocation convenience function |
977
+ | `createLangChainThreadManager` | Thread manager with LangChain `StoredMessage` helpers |
924
978
 
925
979
  **Google GenAI** (`zeitlich/adapters/thread/google-genai`):
926
980
 
927
- | Export | Description |
928
- | ----------------------------------- | ---------------------------------------------------------------------- |
929
- | `createGoogleGenAIAdapter` | Unified adapter returning `createActivities`, `invoker`, `createModelInvoker` |
930
- | `createGoogleGenAIModelInvoker` | Factory that returns a `ModelInvoker` backed by the `@google/genai` SDK |
931
- | `invokeGoogleGenAIModel` | One-shot model invocation convenience function |
932
- | `createGoogleGenAIThreadManager` | Thread manager with Google GenAI `Content` helpers |
981
+ | Export | Description |
982
+ | -------------------------------- | ----------------------------------------------------------------------------- |
983
+ | `createGoogleGenAIAdapter` | Unified adapter returning `createActivities`, `invoker`, `createModelInvoker` |
984
+ | `createGoogleGenAIModelInvoker` | Factory that returns a `ModelInvoker` backed by the `@google/genai` SDK |
985
+ | `invokeGoogleGenAIModel` | One-shot model invocation convenience function |
986
+ | `createGoogleGenAIThreadManager` | Thread manager with Google GenAI `Content` helpers |
933
987
 
934
988
  ### Types
935
989
 
936
- | Export | Description |
937
- | ----------------------- | ---------------------------------------------------------------------------- |
938
- | `AgentStatus` | `"RUNNING" \| "WAITING_FOR_INPUT" \| "COMPLETED" \| "FAILED" \| "CANCELLED"` |
939
- | `MessageContent` | Framework-agnostic message content (`string \| ContentPart[]`) |
940
- | `ToolMessageContent` | Content returned by a tool handler (`string`) |
941
- | `ModelInvoker` | Generic model invocation contract |
942
- | `ModelInvokerConfig` | Configuration passed to a model invoker |
943
- | `ToolDefinition` | Tool definition with name, description, and Zod schema |
944
- | `ToolWithHandler` | Tool definition combined with its handler |
945
- | `RouterContext` | Base context every tool handler receives (`threadId`, `toolCallId`, `toolName`, `sandboxId?`) |
946
- | `Hooks` | Combined session lifecycle + tool execution hooks |
947
- | `ToolRouterHooks` | Narrowed hook interface for tool execution only (pre/post/failure) |
948
- | `ThreadInit` | Thread initialization strategy: `"new"`, `"continue"`, or `"fork"` |
949
- | `SandboxInit` | Sandbox initialization strategy: `"new"`, `"continue"`, `"fork"`, or `"inherit"` |
950
- | `SandboxShutdown` | Sandbox exit policy: `"destroy" \| "pause" \| "keep"` |
951
- | `SubagentSandboxShutdown` | Extended shutdown with `"pause-until-parent-close"` |
952
- | `SubagentSandboxConfig` | Subagent sandbox strategy: `"none" \| "inherit" \| "own" \| { source, shutdown }` |
953
- | `SubagentDefinition` | Callable subagent workflow with embedded metadata (from `defineSubagentWorkflow`) |
954
- | `SubagentConfig` | Resolved subagent configuration consumed by `createSession` |
955
- | `AgentState` | Generic agent state type |
990
+ | Export | Description |
991
+ | ------------------------- | ----------------------------------------------------------------------------------------------------------------- |
992
+ | `AgentStatus` | `"RUNNING" \| "WAITING_FOR_INPUT" \| "COMPLETED" \| "FAILED" \| "CANCELLED"` |
993
+ | `MessageContent` | Framework-agnostic message content (`string \| ContentPart[]`) |
994
+ | `ToolMessageContent` | Content returned by a tool handler (`string`) |
995
+ | `ModelInvoker` | Generic model invocation contract |
996
+ | `ModelInvokerConfig` | Configuration passed to a model invoker |
997
+ | `ToolDefinition` | Tool definition with name, description, and Zod schema |
998
+ | `ToolWithHandler` | Tool definition combined with its handler |
999
+ | `RouterContext` | Base context every tool handler receives (`threadId`, `toolCallId`, `toolName`, `sandboxId?`) |
1000
+ | `Hooks` | Combined session lifecycle + tool execution hooks |
1001
+ | `ToolRouterHooks` | Narrowed hook interface for tool execution only (pre/post/failure) |
1002
+ | `ThreadInit` | Thread initialization strategy: `"new"`, `"continue"`, or `"fork"` |
1003
+ | `SandboxInit` | Sandbox initialization strategy: `"new"`, `"continue"`, `"fork"`, `"from-snapshot"`, or `"inherit"` |
1004
+ | `SandboxShutdown` | Sandbox exit policy: `"destroy" \| "pause" \| "keep" \| "snapshot"` |
1005
+ | `SubagentSandboxShutdown` | Extended shutdown with `"pause-until-parent-close"` |
1006
+ | `SubagentSandboxConfig` | Subagent sandbox strategy: `"none" \| "inherit" \| "own"` with `continuation: "continue" \| "fork" \| "snapshot"` |
1007
+ | `SubagentDefinition` | Callable subagent workflow with embedded metadata (from `defineSubagentWorkflow`) |
1008
+ | `SubagentConfig` | Resolved subagent configuration consumed by `createSession` |
1009
+ | `AgentState` | Generic agent state type |
956
1010
 
957
1011
  ## Architecture
958
1012
 
@@ -1069,7 +1123,7 @@ const sinks: InjectedSinks<ZeitlichObservabilitySinks> = {
1069
1123
  },
1070
1124
  };
1071
1125
 
1072
- const worker = await Worker.create({ sinks, /* ... */ });
1126
+ const worker = await Worker.create({ sinks /* ... */ });
1073
1127
  ```
1074
1128
 
1075
1129
  **2. Wire hooks in your workflow:**
@@ -1,7 +1,7 @@
1
1
  import Redis from 'ioredis';
2
2
  import { Part, Content, GoogleGenAI } from '@google/genai';
3
- import { a as ModelInvoker, P as PrefixedThreadOps, S as ScopedPrefix, R as RouterContext, b as ToolHandlerResponse, c as ActivityToolHandler } from './types-CADc5V_P.js';
4
- import { T as ThreadManagerHooks, P as ProviderThreadManager } from './types-yiXmqedU.js';
3
+ import { a as ModelInvoker, P as PrefixedThreadOps, S as ScopedPrefix, R as RouterContext, b as ToolHandlerResponse, c as ActivityToolHandler } from './types-CPKDl-y_.js';
4
+ import { T as ThreadManagerHooks, P as ProviderThreadManager } from './types-B37hKoWA.js';
5
5
 
6
6
  /** SDK-native content type for Google GenAI human messages */
7
7
  type GoogleGenAIContent = string | Part[];
@@ -1,7 +1,7 @@
1
1
  import Redis from 'ioredis';
2
2
  import { Part, Content, GoogleGenAI } from '@google/genai';
3
- import { a as ModelInvoker, P as PrefixedThreadOps, S as ScopedPrefix, R as RouterContext, b as ToolHandlerResponse, c as ActivityToolHandler } from './types-DQ1l_gXL.cjs';
4
- import { T as ThreadManagerHooks, P as ProviderThreadManager } from './types-Mc_4BCfT.cjs';
3
+ import { a as ModelInvoker, P as PrefixedThreadOps, S as ScopedPrefix, R as RouterContext, b as ToolHandlerResponse, c as ActivityToolHandler } from './types-BO7Yju20.cjs';
4
+ import { T as ThreadManagerHooks, P as ProviderThreadManager } from './types-D08CXPh8.cjs';
5
5
 
6
6
  /** SDK-native content type for Google GenAI human messages */
7
7
  type GoogleGenAIContent = string | Part[];
@@ -29,9 +29,7 @@ async function consumeStream(stream) {
29
29
  event.resourceNotFoundException.message ?? "Resource not found"
30
30
  );
31
31
  if ("validationException" in event && event.validationException)
32
- throw new Error(
33
- event.validationException.message ?? "Validation error"
34
- );
32
+ throw new Error(event.validationException.message ?? "Validation error");
35
33
  if ("internalServerException" in event && event.internalServerException)
36
34
  throw new Error(
37
35
  event.internalServerException.message ?? "Internal server error"
@@ -54,6 +52,9 @@ var BedrockSandboxFileSystem = class {
54
52
  this.sessionId = sessionId;
55
53
  this.workspaceBase = path.posix.resolve("/", workspaceBase);
56
54
  }
55
+ client;
56
+ codeInterpreterIdentifier;
57
+ sessionId;
57
58
  workspaceBase;
58
59
  /**
59
60
  * Resolve a caller-supplied path to an absolute path within the workspace.
@@ -181,9 +182,7 @@ var BedrockSandboxFileSystem = class {
181
182
  async mkdir(path, options) {
182
183
  const norm = this.normalisePath(path);
183
184
  const flag = options?.recursive ? "-p " : "";
184
- const { exitCode, stderr } = await this.execShell(
185
- `mkdir ${flag}"${norm}"`
186
- );
185
+ const { exitCode, stderr } = await this.execShell(`mkdir ${flag}"${norm}"`);
187
186
  if (exitCode !== 0) throw new Error(`mkdir failed: ${stderr}`);
188
187
  }
189
188
  async readdir(path) {
@@ -290,9 +289,7 @@ async function consumeExecStream(stream) {
290
289
  event.resourceNotFoundException.message ?? "Resource not found"
291
290
  );
292
291
  if ("validationException" in event && event.validationException)
293
- throw new Error(
294
- event.validationException.message ?? "Validation error"
295
- );
292
+ throw new Error(event.validationException.message ?? "Validation error");
296
293
  if ("internalServerException" in event && event.internalServerException)
297
294
  throw new Error(
298
295
  event.internalServerException.message ?? "Internal server error"
@@ -321,6 +318,10 @@ var BedrockSandboxImpl = class {
321
318
  workspaceBase
322
319
  );
323
320
  }
321
+ id;
322
+ client;
323
+ codeInterpreterIdentifier;
324
+ sessionId;
324
325
  capabilities = {
325
326
  filesystem: true,
326
327
  execution: true,
@@ -342,8 +343,7 @@ var BedrockSandboxImpl = class {
342
343
  arguments: { command: cmd }
343
344
  })
344
345
  );
345
- if (!resp.stream)
346
- throw new Error("No stream in code interpreter response");
346
+ if (!resp.stream) throw new Error("No stream in code interpreter response");
347
347
  return consumeExecStream(resp.stream);
348
348
  }
349
349
  async destroy() {
@@ -446,6 +446,9 @@ var BedrockSandboxProvider = class {
446
446
  async fork(_sandboxId) {
447
447
  throw new SandboxNotSupportedError("fork");
448
448
  }
449
+ async deleteSnapshot(_snapshot) {
450
+ throw new SandboxNotSupportedError("deleteSnapshot");
451
+ }
449
452
  };
450
453
 
451
454
  exports.BedrockSandboxFileSystem = BedrockSandboxFileSystem;