zeitlich 0.2.35 → 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 (199) 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 +35 -6
  17. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  18. package/dist/adapters/sandbox/daytona/index.d.cts +3 -1
  19. package/dist/adapters/sandbox/daytona/index.d.ts +3 -1
  20. package/dist/adapters/sandbox/daytona/index.js +35 -6
  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/filesystem.ts +29 -6
  121. package/src/adapters/sandbox/daytona/index.ts +6 -0
  122. package/src/adapters/sandbox/daytona/proxy.ts +2 -0
  123. package/src/adapters/sandbox/e2b/filesystem.ts +5 -4
  124. package/src/adapters/sandbox/e2b/index.ts +63 -12
  125. package/src/adapters/sandbox/e2b/proxy.ts +2 -0
  126. package/src/adapters/sandbox/inmemory/index.ts +5 -0
  127. package/src/adapters/sandbox/inmemory/proxy.ts +2 -0
  128. package/src/adapters/thread/anthropic/activities.ts +49 -26
  129. package/src/adapters/thread/anthropic/model-invoker.ts +15 -6
  130. package/src/adapters/thread/anthropic/proxy.ts +6 -2
  131. package/src/adapters/thread/anthropic/thread-manager.test.ts +26 -7
  132. package/src/adapters/thread/anthropic/thread-manager.ts +60 -46
  133. package/src/adapters/thread/google-genai/activities.ts +7 -2
  134. package/src/adapters/thread/google-genai/model-invoker.ts +26 -8
  135. package/src/adapters/thread/google-genai/proxy.ts +6 -2
  136. package/src/adapters/thread/google-genai/thread-manager.test.ts +13 -3
  137. package/src/adapters/thread/google-genai/thread-manager.ts +54 -33
  138. package/src/adapters/thread/langchain/activities.ts +46 -24
  139. package/src/adapters/thread/langchain/hooks.test.ts +36 -49
  140. package/src/adapters/thread/langchain/hooks.ts +18 -5
  141. package/src/adapters/thread/langchain/model-invoker.ts +3 -3
  142. package/src/adapters/thread/langchain/proxy.ts +6 -2
  143. package/src/adapters/thread/langchain/thread-manager.test.ts +5 -1
  144. package/src/adapters/thread/langchain/thread-manager.ts +20 -9
  145. package/src/index.ts +4 -1
  146. package/src/lib/activity.ts +16 -6
  147. package/src/lib/hooks/types.ts +6 -6
  148. package/src/lib/lifecycle.ts +9 -1
  149. package/src/lib/model/proxy.ts +2 -2
  150. package/src/lib/observability/hooks.ts +4 -5
  151. package/src/lib/observability/index.ts +1 -4
  152. package/src/lib/sandbox/manager.ts +21 -4
  153. package/src/lib/sandbox/node-fs.ts +3 -6
  154. package/src/lib/sandbox/sandbox.test.ts +36 -3
  155. package/src/lib/sandbox/tree.integration.test.ts +10 -3
  156. package/src/lib/sandbox/types.ts +35 -1
  157. package/src/lib/session/session-edge-cases.integration.test.ts +51 -13
  158. package/src/lib/session/session.integration.test.ts +139 -0
  159. package/src/lib/session/session.ts +50 -19
  160. package/src/lib/session/types.ts +13 -5
  161. package/src/lib/skills/fs-provider.ts +12 -8
  162. package/src/lib/skills/handler.ts +1 -1
  163. package/src/lib/skills/parse.ts +3 -1
  164. package/src/lib/skills/register.ts +1 -3
  165. package/src/lib/skills/skills.integration.test.ts +25 -15
  166. package/src/lib/state/manager.integration.test.ts +12 -2
  167. package/src/lib/subagent/define.ts +1 -1
  168. package/src/lib/subagent/handler.ts +186 -71
  169. package/src/lib/subagent/index.ts +1 -5
  170. package/src/lib/subagent/register.ts +3 -2
  171. package/src/lib/subagent/signals.ts +1 -10
  172. package/src/lib/subagent/subagent.integration.test.ts +438 -156
  173. package/src/lib/subagent/tool.ts +4 -3
  174. package/src/lib/subagent/types.ts +50 -20
  175. package/src/lib/subagent/workflow.ts +9 -49
  176. package/src/lib/thread/id.test.ts +1 -1
  177. package/src/lib/thread/id.ts +1 -2
  178. package/src/lib/thread/proxy.ts +3 -4
  179. package/src/lib/thread/types.ts +11 -3
  180. package/src/lib/tool-router/index.ts +1 -5
  181. package/src/lib/tool-router/router-edge-cases.integration.test.ts +1 -1
  182. package/src/lib/tool-router/router.ts +3 -2
  183. package/src/lib/tool-router/types.ts +11 -3
  184. package/src/lib/tool-router/with-sandbox.ts +19 -5
  185. package/src/lib/virtual-fs/filesystem.ts +1 -1
  186. package/src/lib/virtual-fs/index.ts +5 -1
  187. package/src/lib/virtual-fs/mutations.ts +2 -4
  188. package/src/lib/virtual-fs/queries.ts +9 -5
  189. package/src/lib/virtual-fs/types.ts +4 -1
  190. package/src/lib/virtual-fs/virtual-fs.test.ts +9 -11
  191. package/src/lib/workflow.test.ts +7 -4
  192. package/src/lib/workflow.ts +1 -5
  193. package/src/tools/ask-user-question/tool.ts +1 -3
  194. package/src/tools/glob/handler.ts +1 -4
  195. package/src/tools/task-get/handler.ts +4 -5
  196. package/src/tools/task-list/handler.ts +1 -4
  197. package/src/tools/task-update/handler.ts +4 -5
  198. package/src/workflow.ts +20 -7
  199. package/tsup.config.ts +9 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zeitlich",
3
- "version": "0.2.35",
3
+ "version": "0.2.37",
4
4
  "description": "[EXPERIMENTAL] An opinionated AI agent implementation for Temporal",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -26,9 +26,7 @@ async function consumeStream(
26
26
  event.resourceNotFoundException.message ?? "Resource not found"
27
27
  );
28
28
  if ("validationException" in event && event.validationException)
29
- throw new Error(
30
- event.validationException.message ?? "Validation error"
31
- );
29
+ throw new Error(event.validationException.message ?? "Validation error");
32
30
  if ("internalServerException" in event && event.internalServerException)
33
31
  throw new Error(
34
32
  event.internalServerException.message ?? "Internal server error"
@@ -185,9 +183,7 @@ export class BedrockSandboxFileSystem implements SandboxFileSystem {
185
183
  async appendFile(path: string, content: string | Uint8Array): Promise<void> {
186
184
  const norm = this.normalisePath(path);
187
185
  const addition =
188
- typeof content === "string"
189
- ? content
190
- : new TextDecoder().decode(content);
186
+ typeof content === "string" ? content : new TextDecoder().decode(content);
191
187
  const escaped = addition.replace(/'/g, "'\\''");
192
188
  const { exitCode, stderr } = await this.execShell(
193
189
  `printf '%s' '${escaped}' >> "${norm}"`
@@ -227,9 +223,7 @@ export class BedrockSandboxFileSystem implements SandboxFileSystem {
227
223
  async mkdir(path: string, options?: { recursive?: boolean }): Promise<void> {
228
224
  const norm = this.normalisePath(path);
229
225
  const flag = options?.recursive ? "-p " : "";
230
- const { exitCode, stderr } = await this.execShell(
231
- `mkdir ${flag}"${norm}"`
232
- );
226
+ const { exitCode, stderr } = await this.execShell(`mkdir ${flag}"${norm}"`);
233
227
  if (exitCode !== 0) throw new Error(`mkdir failed: ${stderr}`);
234
228
  }
235
229
 
@@ -287,7 +281,8 @@ export class BedrockSandboxFileSystem implements SandboxFileSystem {
287
281
  ): Promise<void> {
288
282
  const norm = this.normalisePath(path);
289
283
  if (options?.recursive || options?.force) {
290
- const flags = `${options?.recursive ? "-r" : ""} ${options?.force ? "-f" : ""}`.trim();
284
+ const flags =
285
+ `${options?.recursive ? "-r" : ""} ${options?.force ? "-f" : ""}`.trim();
291
286
  const { exitCode, stderr } = await this.execShell(
292
287
  `rm ${flags} "${norm}"`
293
288
  );
@@ -301,8 +296,7 @@ export class BedrockSandboxFileSystem implements SandboxFileSystem {
301
296
  paths: [rel],
302
297
  });
303
298
  if (result.isError) {
304
- const msg =
305
- result.content?.map((b) => b.text).join("") ?? "rm failed";
299
+ const msg = result.content?.map((b) => b.text).join("") ?? "rm failed";
306
300
  throw new Error(msg);
307
301
  }
308
302
  }
@@ -49,9 +49,7 @@ async function consumeExecStream(
49
49
  event.resourceNotFoundException.message ?? "Resource not found"
50
50
  );
51
51
  if ("validationException" in event && event.validationException)
52
- throw new Error(
53
- event.validationException.message ?? "Validation error"
54
- );
52
+ throw new Error(event.validationException.message ?? "Validation error");
55
53
  if ("internalServerException" in event && event.internalServerException)
56
54
  throw new Error(
57
55
  event.internalServerException.message ?? "Internal server error"
@@ -118,8 +116,7 @@ class BedrockSandboxImpl implements Sandbox {
118
116
  })
119
117
  );
120
118
 
121
- if (!resp.stream)
122
- throw new Error("No stream in code interpreter response");
119
+ if (!resp.stream) throw new Error("No stream in code interpreter response");
123
120
  return consumeExecStream(resp.stream);
124
121
  }
125
122
 
@@ -137,9 +134,10 @@ class BedrockSandboxImpl implements Sandbox {
137
134
  // BedrockSandboxProvider
138
135
  // ============================================================================
139
136
 
140
- export class BedrockSandboxProvider
141
- implements SandboxProvider<BedrockSandboxCreateOptions, BedrockSandbox>
142
- {
137
+ export class BedrockSandboxProvider implements SandboxProvider<
138
+ BedrockSandboxCreateOptions,
139
+ BedrockSandbox
140
+ > {
143
141
  readonly id = "bedrock";
144
142
  readonly capabilities: SandboxCapabilities = {
145
143
  filesystem: true,
@@ -252,6 +250,10 @@ export class BedrockSandboxProvider
252
250
  async fork(_sandboxId: string): Promise<Sandbox> {
253
251
  throw new SandboxNotSupportedError("fork");
254
252
  }
253
+
254
+ async deleteSnapshot(_snapshot: SandboxSnapshot): Promise<void> {
255
+ throw new SandboxNotSupportedError("deleteSnapshot");
256
+ }
255
257
  }
256
258
 
257
259
  // Re-exports
@@ -52,6 +52,8 @@ export function proxyBedrockSandboxOps(
52
52
  pauseSandbox: acts[p("pauseSandbox")],
53
53
  resumeSandbox: acts[p("resumeSandbox")],
54
54
  snapshotSandbox: acts[p("snapshotSandbox")],
55
+ restoreSandbox: acts[p("restoreSandbox")],
56
+ deleteSandboxSnapshot: acts[p("deleteSandboxSnapshot")],
55
57
  forkSandbox: acts[p("forkSandbox")],
56
58
  } as SandboxOps<BedrockSandboxCreateOptions>;
57
59
  }
@@ -28,6 +28,28 @@ export class DaytonaSandboxFileSystem implements SandboxFileSystem {
28
28
  return posix.resolve(this.workspaceBase, path);
29
29
  }
30
30
 
31
+ private async ensureParentDirs(absolutePaths: string[]): Promise<void> {
32
+ const prefix = this.workspaceBase === "/" ? "/" : `${this.workspaceBase}/`;
33
+ const dirs = new Set<string>();
34
+ for (const p of absolutePaths) {
35
+ let dir = posix.dirname(p);
36
+ while (dir.startsWith(prefix)) {
37
+ dirs.add(dir);
38
+ dir = posix.dirname(dir);
39
+ }
40
+ }
41
+ const sorted = [...dirs].sort(
42
+ (a, b) => a.split("/").length - b.split("/").length
43
+ );
44
+ for (const dir of sorted) {
45
+ try {
46
+ await this.sandbox.fs.createFolder(dir, "755");
47
+ } catch {
48
+ // Folder already exists — Daytona's createFolder throws in that case.
49
+ }
50
+ }
51
+ }
52
+
31
53
  async readFile(path: string): Promise<string> {
32
54
  const norm = this.normalisePath(path);
33
55
  const buf = await this.sandbox.fs.downloadFile(norm);
@@ -46,18 +68,19 @@ export class DaytonaSandboxFileSystem implements SandboxFileSystem {
46
68
  typeof content === "string"
47
69
  ? Buffer.from(content, "utf-8")
48
70
  : Buffer.from(content);
71
+ await this.ensureParentDirs([norm]);
49
72
  await this.sandbox.fs.uploadFile(buf, norm);
50
73
  }
51
74
 
52
75
  async writeFiles(
53
76
  files: { path: string; content: string | Uint8Array }[]
54
77
  ): Promise<void> {
55
- await this.sandbox.fs.uploadFiles(
56
- files.map((f) => ({
57
- source: Buffer.from(f.content),
58
- destination: this.normalisePath(f.path),
59
- }))
60
- );
78
+ const uploads = files.map((f) => ({
79
+ source: Buffer.from(f.content),
80
+ destination: this.normalisePath(f.path),
81
+ }));
82
+ await this.ensureParentDirs(uploads.map((u) => u.destination));
83
+ await this.sandbox.fs.uploadFiles(uploads);
61
84
  }
62
85
 
63
86
  async appendFile(path: string, content: string | Uint8Array): Promise<void> {
@@ -164,6 +164,12 @@ export class DaytonaSandboxProvider implements SandboxProvider<
164
164
  "restore (use Daytona's native snapshot API directly)"
165
165
  );
166
166
  }
167
+
168
+ async deleteSnapshot(_snapshot: SandboxSnapshot): Promise<void> {
169
+ throw new SandboxNotSupportedError(
170
+ "deleteSnapshot (use Daytona's native snapshot API directly)"
171
+ );
172
+ }
167
173
  }
168
174
 
169
175
  // Re-exports
@@ -52,6 +52,8 @@ export function proxyDaytonaSandboxOps(
52
52
  pauseSandbox: acts[p("pauseSandbox")],
53
53
  resumeSandbox: acts[p("resumeSandbox")],
54
54
  snapshotSandbox: acts[p("snapshotSandbox")],
55
+ restoreSandbox: acts[p("restoreSandbox")],
56
+ deleteSandboxSnapshot: acts[p("deleteSandboxSnapshot")],
55
57
  forkSandbox: acts[p("forkSandbox")],
56
58
  } as SandboxOps<DaytonaSandboxCreateOptions>;
57
59
  }
@@ -7,7 +7,10 @@ import type {
7
7
  import { posix } from "node:path";
8
8
 
9
9
  function toArrayBuffer(u8: Uint8Array): ArrayBuffer {
10
- return u8.buffer.slice(u8.byteOffset, u8.byteOffset + u8.byteLength) as ArrayBuffer;
10
+ return u8.buffer.slice(
11
+ u8.byteOffset,
12
+ u8.byteOffset + u8.byteLength
13
+ ) as ArrayBuffer;
11
14
  }
12
15
 
13
16
  /**
@@ -59,9 +62,7 @@ export class E2bSandboxFileSystem implements SandboxFileSystem {
59
62
  // file doesn't exist yet — write from scratch
60
63
  }
61
64
  const addition =
62
- typeof content === "string"
63
- ? content
64
- : new TextDecoder().decode(content);
65
+ typeof content === "string" ? content : new TextDecoder().decode(content);
65
66
  await this.sandbox.files.write(norm, existing + addition);
66
67
  }
67
68
 
@@ -62,9 +62,10 @@ class E2bSandboxImpl implements Sandbox {
62
62
  // E2bSandboxProvider
63
63
  // ============================================================================
64
64
 
65
- export class E2bSandboxProvider
66
- implements SandboxProvider<E2bSandboxCreateOptions, E2bSandbox>
67
- {
65
+ export class E2bSandboxProvider implements SandboxProvider<
66
+ E2bSandboxCreateOptions,
67
+ E2bSandbox
68
+ > {
68
69
  readonly id = "e2b";
69
70
  readonly capabilities: SandboxCapabilities = {
70
71
  filesystem: true,
@@ -87,10 +88,7 @@ export class E2bSandboxProvider
87
88
  ): Promise<SandboxCreateResult> {
88
89
  const template = options?.template ?? this.defaultTemplate;
89
90
  const workspaceBase = this.defaultWorkspaceBase;
90
- const createOpts = {
91
- envs: options?.env,
92
- timeoutMs: options?.timeoutMs ?? this.defaultTimeoutMs,
93
- };
91
+ const createOpts = this.buildSdkCreateOpts(options);
94
92
 
95
93
  const sdkSandbox = template
96
94
  ? await E2bSdkSandbox.create(template, createOpts)
@@ -116,7 +114,11 @@ export class E2bSandboxProvider
116
114
  async get(sandboxId: string): Promise<E2bSandbox> {
117
115
  try {
118
116
  const sdkSandbox = await E2bSdkSandbox.connect(sandboxId);
119
- return new E2bSandboxImpl(sandboxId, sdkSandbox, this.defaultWorkspaceBase);
117
+ return new E2bSandboxImpl(
118
+ sandboxId,
119
+ sdkSandbox,
120
+ this.defaultWorkspaceBase
121
+ );
120
122
  } catch {
121
123
  throw new SandboxNotFoundError(sandboxId);
122
124
  }
@@ -140,12 +142,39 @@ export class E2bSandboxProvider
140
142
  await E2bSdkSandbox.connect(sandboxId);
141
143
  }
142
144
 
143
- async snapshot(_sandboxId: string): Promise<SandboxSnapshot> {
144
- throw new SandboxNotSupportedError("snapshot");
145
+ async snapshot(sandboxId: string): Promise<SandboxSnapshot> {
146
+ const { snapshotId } = await E2bSdkSandbox.createSnapshot(sandboxId);
147
+ return {
148
+ sandboxId,
149
+ providerId: this.id,
150
+ data: { snapshotId },
151
+ createdAt: new Date().toISOString(),
152
+ };
153
+ }
154
+
155
+ async restore(snapshot: SandboxSnapshot): Promise<Sandbox> {
156
+ const data = snapshot.data as { snapshotId?: string } | null;
157
+ if (!data?.snapshotId) {
158
+ throw new SandboxNotSupportedError(
159
+ "restore: snapshot is missing snapshotId"
160
+ );
161
+ }
162
+ const sdkSandbox = await E2bSdkSandbox.create(data.snapshotId);
163
+ return new E2bSandboxImpl(
164
+ sdkSandbox.sandboxId,
165
+ sdkSandbox,
166
+ this.defaultWorkspaceBase
167
+ );
145
168
  }
146
169
 
147
- async restore(_snapshot: SandboxSnapshot): Promise<Sandbox> {
148
- throw new SandboxNotSupportedError("restore");
170
+ async deleteSnapshot(snapshot: SandboxSnapshot): Promise<void> {
171
+ const data = snapshot.data as { snapshotId?: string } | null;
172
+ if (!data?.snapshotId) return;
173
+ try {
174
+ await E2bSdkSandbox.deleteSnapshot(data.snapshotId);
175
+ } catch {
176
+ // Already deleted or no longer accessible — treat as no-op.
177
+ }
149
178
  }
150
179
 
151
180
  async fork(sandboxId: string): Promise<Sandbox> {
@@ -157,6 +186,28 @@ export class E2bSandboxProvider
157
186
  this.defaultWorkspaceBase
158
187
  );
159
188
  }
189
+
190
+ private buildSdkCreateOpts(options?: E2bSandboxCreateOptions) {
191
+ return {
192
+ envs: options?.env,
193
+ timeoutMs: options?.timeoutMs ?? this.defaultTimeoutMs,
194
+ metadata: options?.metadata,
195
+ allowInternetAccess: options?.allowInternetAccess,
196
+ network: options?.network
197
+ ? {
198
+ allowOut: options.network.allowOut,
199
+ denyOut: options.network.denyOut,
200
+ allowPublicTraffic: options.network.allowPublicTraffic,
201
+ }
202
+ : undefined,
203
+ lifecycle: options?.lifecycle
204
+ ? {
205
+ onTimeout: options.lifecycle.onTimeout,
206
+ autoResume: options.lifecycle.autoResume,
207
+ }
208
+ : undefined,
209
+ };
210
+ }
160
211
  }
161
212
 
162
213
  // Re-exports
@@ -52,6 +52,8 @@ export function proxyE2bSandboxOps(
52
52
  pauseSandbox: acts[p("pauseSandbox")],
53
53
  resumeSandbox: acts[p("resumeSandbox")],
54
54
  snapshotSandbox: acts[p("snapshotSandbox")],
55
+ restoreSandbox: acts[p("restoreSandbox")],
56
+ deleteSandboxSnapshot: acts[p("deleteSandboxSnapshot")],
55
57
  forkSandbox: acts[p("forkSandbox")],
56
58
  } as SandboxOps<E2bSandboxCreateOptions>;
57
59
  }
@@ -244,4 +244,9 @@ export class InMemorySandboxProvider implements SandboxProvider {
244
244
  this.sandboxes.set(sandbox.id, sandbox);
245
245
  return sandbox;
246
246
  }
247
+
248
+ async deleteSnapshot(_snapshot: SandboxSnapshot): Promise<void> {
249
+ // In-memory snapshots are opaque data held by the caller — nothing to
250
+ // delete on the provider side.
251
+ }
247
252
  }
@@ -49,6 +49,8 @@ export function proxyInMemorySandboxOps(
49
49
  pauseSandbox: acts[p("pauseSandbox")],
50
50
  resumeSandbox: acts[p("resumeSandbox")],
51
51
  snapshotSandbox: acts[p("snapshotSandbox")],
52
+ restoreSandbox: acts[p("restoreSandbox")],
53
+ deleteSandboxSnapshot: acts[p("deleteSandboxSnapshot")],
52
54
  forkSandbox: acts[p("forkSandbox")],
53
55
  } as SandboxOps;
54
56
  }
@@ -25,8 +25,10 @@ import {
25
25
 
26
26
  const ADAPTER_PREFIX = "anthropic" as const;
27
27
 
28
- export type AnthropicThreadOps<TScope extends string = ""> =
29
- PrefixedThreadOps<ScopedPrefix<TScope, typeof ADAPTER_PREFIX>, AnthropicContent>;
28
+ export type AnthropicThreadOps<TScope extends string = ""> = PrefixedThreadOps<
29
+ ScopedPrefix<TScope, typeof ADAPTER_PREFIX>,
30
+ AnthropicContent
31
+ >;
30
32
 
31
33
  export interface AnthropicAdapterConfig {
32
34
  redis: Redis;
@@ -47,7 +49,8 @@ export interface AnthropicAdapterConfig {
47
49
  * (e.g. `{ type: "text", text: "..." }`, `{ type: "image", source: { ... } }`).
48
50
  * Passed through as-is to the `tool_result` block.
49
51
  */
50
- export type AnthropicToolResponse = Anthropic.Messages.ToolResultBlockParam["content"];
52
+ export type AnthropicToolResponse =
53
+ Anthropic.Messages.ToolResultBlockParam["content"];
51
54
 
52
55
  export interface AnthropicAdapter {
53
56
  /** Model invoker using the default model (only available when `model` was provided) */
@@ -55,7 +58,7 @@ export interface AnthropicAdapter {
55
58
  /** Create an invoker for a specific model name (for multi-model setups) */
56
59
  createModelInvoker(
57
60
  model: string,
58
- maxTokens?: number,
61
+ maxTokens?: number
59
62
  ): ModelInvoker<Anthropic.Messages.Message>;
60
63
  /**
61
64
  * Create prefixed thread activities for registration on the worker.
@@ -72,9 +75,7 @@ export interface AnthropicAdapter {
72
75
  * // → { anthropicResearchAgentInitializeThread, … }
73
76
  * ```
74
77
  */
75
- createActivities<S extends string = "">(
76
- scope?: S,
77
- ): AnthropicThreadOps<S>;
78
+ createActivities<S extends string = "">(scope?: S): AnthropicThreadOps<S>;
78
79
 
79
80
  /**
80
81
  * Identity wrapper that types a tool handler for this adapter.
@@ -83,8 +84,8 @@ export interface AnthropicAdapter {
83
84
  wrapHandler<TArgs, TResult, TContext extends RouterContext = RouterContext>(
84
85
  handler: (
85
86
  args: TArgs,
86
- context: TContext,
87
- ) => Promise<ToolHandlerResponse<TResult, AnthropicToolResponse>>,
87
+ context: TContext
88
+ ) => Promise<ToolHandlerResponse<TResult, AnthropicToolResponse>>
88
89
  ): ActivityToolHandler<TArgs, TResult, TContext, AnthropicToolResponse>;
89
90
  }
90
91
 
@@ -130,13 +131,20 @@ export interface AnthropicAdapter {
130
131
  * ```
131
132
  */
132
133
  export function createAnthropicAdapter(
133
- config: AnthropicAdapterConfig,
134
+ config: AnthropicAdapterConfig
134
135
  ): AnthropicAdapter {
135
136
  const { redis, client } = config;
136
137
 
137
138
  const threadOps: ThreadOps<AnthropicContent> = {
138
- async initializeThread(threadId: string, threadKey?: string): Promise<void> {
139
- const thread = createAnthropicThreadManager({ redis, threadId, key: threadKey });
139
+ async initializeThread(
140
+ threadId: string,
141
+ threadKey?: string
142
+ ): Promise<void> {
143
+ const thread = createAnthropicThreadManager({
144
+ redis,
145
+ threadId,
146
+ key: threadKey,
147
+ });
140
148
  await thread.initialize();
141
149
  },
142
150
 
@@ -144,9 +152,13 @@ export function createAnthropicAdapter(
144
152
  threadId: string,
145
153
  id: string,
146
154
  content: AnthropicContent,
147
- threadKey?: string,
155
+ threadKey?: string
148
156
  ): Promise<void> {
149
- const thread = createAnthropicThreadManager({ redis, threadId, key: threadKey });
157
+ const thread = createAnthropicThreadManager({
158
+ redis,
159
+ threadId,
160
+ key: threadKey,
161
+ });
150
162
  await thread.appendUserMessage(id, content);
151
163
  },
152
164
 
@@ -154,15 +166,23 @@ export function createAnthropicAdapter(
154
166
  threadId: string,
155
167
  id: string,
156
168
  content: AnthropicSystemContent,
157
- threadKey?: string,
169
+ threadKey?: string
158
170
  ): Promise<void> {
159
- const thread = createAnthropicThreadManager({ redis, threadId, key: threadKey });
171
+ const thread = createAnthropicThreadManager({
172
+ redis,
173
+ threadId,
174
+ key: threadKey,
175
+ });
160
176
  await thread.appendSystemMessage(id, content);
161
177
  },
162
178
 
163
179
  async appendToolResult(id: string, cfg: ToolResultConfig): Promise<void> {
164
180
  const { threadId, threadKey, toolCallId, toolName, content } = cfg;
165
- const thread = createAnthropicThreadManager({ redis, threadId, key: threadKey });
181
+ const thread = createAnthropicThreadManager({
182
+ redis,
183
+ threadId,
184
+ key: threadKey,
185
+ });
166
186
  await thread.appendToolResult(id, toolCallId, toolName, content);
167
187
  },
168
188
 
@@ -170,16 +190,20 @@ export function createAnthropicAdapter(
170
190
  threadId: string,
171
191
  id: string,
172
192
  message: Anthropic.Messages.Message,
173
- threadKey?: string,
193
+ threadKey?: string
174
194
  ): Promise<void> {
175
- const thread = createAnthropicThreadManager({ redis, threadId, key: threadKey });
195
+ const thread = createAnthropicThreadManager({
196
+ redis,
197
+ threadId,
198
+ key: threadKey,
199
+ });
176
200
  await thread.appendAssistantMessage(id, message.content);
177
201
  },
178
202
 
179
203
  async forkThread(
180
204
  sourceThreadId: string,
181
205
  targetThreadId: string,
182
- threadKey?: string,
206
+ threadKey?: string
183
207
  ): Promise<void> {
184
208
  const thread = createAnthropicThreadManager({
185
209
  redis,
@@ -191,21 +215,20 @@ export function createAnthropicAdapter(
191
215
  };
192
216
 
193
217
  function createActivities<S extends string = "">(
194
- scope?: S,
218
+ scope?: S
195
219
  ): AnthropicThreadOps<S> {
196
220
  const prefix = scope
197
221
  ? `${ADAPTER_PREFIX}${scope.charAt(0).toUpperCase()}${scope.slice(1)}`
198
222
  : ADAPTER_PREFIX;
199
- const cap = (s: string): string =>
200
- s.charAt(0).toUpperCase() + s.slice(1);
223
+ const cap = (s: string): string => s.charAt(0).toUpperCase() + s.slice(1);
201
224
  return Object.fromEntries(
202
- Object.entries(threadOps).map(([k, v]) => [`${prefix}${cap(k)}`, v]),
225
+ Object.entries(threadOps).map(([k, v]) => [`${prefix}${cap(k)}`, v])
203
226
  ) as AnthropicThreadOps<S>;
204
227
  }
205
228
 
206
229
  const makeInvoker = (
207
230
  model: string,
208
- maxTokens?: number,
231
+ maxTokens?: number
209
232
  ): ModelInvoker<Anthropic.Messages.Message> => {
210
233
  const invokerConfig: AnthropicModelInvokerConfig = {
211
234
  redis,
@@ -225,7 +248,7 @@ export function createAnthropicAdapter(
225
248
  : ((() => {
226
249
  throw new Error(
227
250
  "No default model provided to createAnthropicAdapter. " +
228
- "Either pass `model` in the config or use `createModelInvoker(model)` instead.",
251
+ "Either pass `model` in the config or use `createModelInvoker(model)` instead."
229
252
  );
230
253
  }) as unknown as ModelInvoker<Anthropic.Messages.Message>);
231
254
 
@@ -2,7 +2,10 @@ import type Redis from "ioredis";
2
2
  import type Anthropic from "@anthropic-ai/sdk";
3
3
  import type { SerializableToolDefinition } from "../../../lib/types";
4
4
  import type { AgentResponse, ModelInvokerConfig } from "../../../lib/model";
5
- import { createAnthropicThreadManager, type AnthropicThreadManagerHooks } from "./thread-manager";
5
+ import {
6
+ createAnthropicThreadManager,
7
+ type AnthropicThreadManagerHooks,
8
+ } from "./thread-manager";
6
9
  import { getActivityContext } from "../../../lib/activity";
7
10
 
8
11
  export interface AnthropicModelInvokerConfig {
@@ -15,7 +18,7 @@ export interface AnthropicModelInvokerConfig {
15
18
  }
16
19
 
17
20
  function toAnthropicTools(
18
- tools: SerializableToolDefinition[],
21
+ tools: SerializableToolDefinition[]
19
22
  ): Anthropic.Messages.Tool[] {
20
23
  return tools.map((t) => ({
21
24
  name: t.name,
@@ -56,12 +59,17 @@ export function createAnthropicModelInvoker({
56
59
  hooks,
57
60
  }: AnthropicModelInvokerConfig) {
58
61
  return async function invokeAnthropicModel(
59
- config: ModelInvokerConfig,
62
+ config: ModelInvokerConfig
60
63
  ): Promise<AgentResponse<Anthropic.Messages.Message>> {
61
64
  const { threadId, threadKey, state } = config;
62
65
  const { heartbeat, signal } = getActivityContext();
63
66
 
64
- const thread = createAnthropicThreadManager({ redis, threadId, key: threadKey, hooks });
67
+ const thread = createAnthropicThreadManager({
68
+ redis,
69
+ threadId,
70
+ key: threadKey,
71
+ hooks,
72
+ });
65
73
  const { messages, system } = await thread.prepareForInvocation();
66
74
 
67
75
  const anthropicTools = toAnthropicTools(state.tools);
@@ -85,7 +93,7 @@ export function createAnthropicModelInvoker({
85
93
 
86
94
  const toolCalls = response.content.filter(
87
95
  (block): block is Anthropic.Messages.ToolUseBlock =>
88
- block.type === "tool_use",
96
+ block.type === "tool_use"
89
97
  );
90
98
 
91
99
  return {
@@ -98,7 +106,8 @@ export function createAnthropicModelInvoker({
98
106
  usage: {
99
107
  inputTokens: response.usage.input_tokens,
100
108
  outputTokens: response.usage.output_tokens,
101
- cachedWriteTokens: response.usage.cache_creation_input_tokens ?? undefined,
109
+ cachedWriteTokens:
110
+ response.usage.cache_creation_input_tokens ?? undefined,
102
111
  cachedReadTokens: response.usage.cache_read_input_tokens ?? undefined,
103
112
  },
104
113
  };
@@ -27,7 +27,11 @@ const ADAPTER_PREFIX = "anthropic";
27
27
 
28
28
  export function proxyAnthropicThreadOps(
29
29
  scope?: string,
30
- options?: Parameters<typeof createThreadOpsProxy>[2],
30
+ options?: Parameters<typeof createThreadOpsProxy>[2]
31
31
  ): ActivityInterfaceFor<ThreadOps<AnthropicContent>> {
32
- return createThreadOpsProxy(ADAPTER_PREFIX, scope, options) as ActivityInterfaceFor<ThreadOps<AnthropicContent>>;
32
+ return createThreadOpsProxy(
33
+ ADAPTER_PREFIX,
34
+ scope,
35
+ options
36
+ ) as ActivityInterfaceFor<ThreadOps<AnthropicContent>>;
33
37
  }