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.
- package/README.md +146 -92
- package/dist/{activities-BVI2lTwr.d.ts → activities-Bb-nAjwQ.d.ts} +2 -2
- package/dist/{activities-hd4aNnZE.d.cts → activities-vkI4_3CC.d.cts} +2 -2
- package/dist/adapters/sandbox/bedrock/index.cjs +14 -11
- package/dist/adapters/sandbox/bedrock/index.cjs.map +1 -1
- package/dist/adapters/sandbox/bedrock/index.d.cts +4 -3
- package/dist/adapters/sandbox/bedrock/index.d.ts +4 -3
- package/dist/adapters/sandbox/bedrock/index.js +14 -11
- package/dist/adapters/sandbox/bedrock/index.js.map +1 -1
- package/dist/adapters/sandbox/bedrock/workflow.cjs +2 -0
- package/dist/adapters/sandbox/bedrock/workflow.cjs.map +1 -1
- package/dist/adapters/sandbox/bedrock/workflow.d.cts +2 -2
- package/dist/adapters/sandbox/bedrock/workflow.d.ts +2 -2
- package/dist/adapters/sandbox/bedrock/workflow.js +2 -0
- package/dist/adapters/sandbox/bedrock/workflow.js.map +1 -1
- package/dist/adapters/sandbox/daytona/index.cjs +35 -6
- package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
- package/dist/adapters/sandbox/daytona/index.d.cts +3 -1
- package/dist/adapters/sandbox/daytona/index.d.ts +3 -1
- package/dist/adapters/sandbox/daytona/index.js +35 -6
- package/dist/adapters/sandbox/daytona/index.js.map +1 -1
- package/dist/adapters/sandbox/daytona/workflow.cjs +2 -0
- package/dist/adapters/sandbox/daytona/workflow.cjs.map +1 -1
- package/dist/adapters/sandbox/daytona/workflow.d.cts +1 -1
- package/dist/adapters/sandbox/daytona/workflow.d.ts +1 -1
- package/dist/adapters/sandbox/daytona/workflow.js +2 -0
- package/dist/adapters/sandbox/daytona/workflow.js.map +1 -1
- package/dist/adapters/sandbox/e2b/index.cjs +59 -10
- package/dist/adapters/sandbox/e2b/index.cjs.map +1 -1
- package/dist/adapters/sandbox/e2b/index.d.cts +5 -3
- package/dist/adapters/sandbox/e2b/index.d.ts +5 -3
- package/dist/adapters/sandbox/e2b/index.js +59 -10
- package/dist/adapters/sandbox/e2b/index.js.map +1 -1
- package/dist/adapters/sandbox/e2b/workflow.cjs +2 -0
- package/dist/adapters/sandbox/e2b/workflow.cjs.map +1 -1
- package/dist/adapters/sandbox/e2b/workflow.d.cts +1 -1
- package/dist/adapters/sandbox/e2b/workflow.d.ts +1 -1
- package/dist/adapters/sandbox/e2b/workflow.js +2 -0
- package/dist/adapters/sandbox/e2b/workflow.js.map +1 -1
- package/dist/adapters/sandbox/inmemory/index.cjs +5 -0
- package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
- package/dist/adapters/sandbox/inmemory/index.d.cts +2 -1
- package/dist/adapters/sandbox/inmemory/index.d.ts +2 -1
- package/dist/adapters/sandbox/inmemory/index.js +5 -0
- package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
- package/dist/adapters/sandbox/inmemory/workflow.cjs +2 -0
- package/dist/adapters/sandbox/inmemory/workflow.cjs.map +1 -1
- package/dist/adapters/sandbox/inmemory/workflow.d.cts +1 -1
- package/dist/adapters/sandbox/inmemory/workflow.d.ts +1 -1
- package/dist/adapters/sandbox/inmemory/workflow.js +2 -0
- package/dist/adapters/sandbox/inmemory/workflow.js.map +1 -1
- package/dist/adapters/thread/anthropic/index.cjs +71 -36
- package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/index.d.cts +5 -5
- package/dist/adapters/thread/anthropic/index.d.ts +5 -5
- package/dist/adapters/thread/anthropic/index.js +71 -36
- package/dist/adapters/thread/anthropic/index.js.map +1 -1
- package/dist/adapters/thread/anthropic/workflow.cjs +5 -1
- package/dist/adapters/thread/anthropic/workflow.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/workflow.d.cts +5 -5
- package/dist/adapters/thread/anthropic/workflow.d.ts +5 -5
- package/dist/adapters/thread/anthropic/workflow.js +5 -1
- package/dist/adapters/thread/anthropic/workflow.js.map +1 -1
- package/dist/adapters/thread/google-genai/index.cjs +50 -25
- package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/index.d.cts +5 -5
- package/dist/adapters/thread/google-genai/index.d.ts +5 -5
- package/dist/adapters/thread/google-genai/index.js +50 -25
- package/dist/adapters/thread/google-genai/index.js.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.cjs +5 -1
- package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.d.cts +5 -5
- package/dist/adapters/thread/google-genai/workflow.d.ts +5 -5
- package/dist/adapters/thread/google-genai/workflow.js +5 -1
- package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
- package/dist/adapters/thread/langchain/index.cjs +34 -7
- package/dist/adapters/thread/langchain/index.cjs.map +1 -1
- package/dist/adapters/thread/langchain/index.d.cts +5 -5
- package/dist/adapters/thread/langchain/index.d.ts +5 -5
- package/dist/adapters/thread/langchain/index.js +34 -7
- package/dist/adapters/thread/langchain/index.js.map +1 -1
- package/dist/adapters/thread/langchain/workflow.cjs +5 -1
- package/dist/adapters/thread/langchain/workflow.cjs.map +1 -1
- package/dist/adapters/thread/langchain/workflow.d.cts +5 -5
- package/dist/adapters/thread/langchain/workflow.d.ts +5 -5
- package/dist/adapters/thread/langchain/workflow.js +5 -1
- package/dist/adapters/thread/langchain/workflow.js.map +1 -1
- package/dist/index.cjs +206 -120
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -11
- package/dist/index.d.ts +17 -11
- package/dist/index.js +207 -121
- package/dist/index.js.map +1 -1
- package/dist/{proxy-BjdFGPTm.d.ts → proxy-0smGKvx8.d.ts} +1 -1
- package/dist/{proxy-7RnVaPdJ.d.cts → proxy-DEtowJyd.d.cts} +1 -1
- package/dist/{thread-manager-DjN5JYul.d.ts → thread-manager-3fszQih4.d.ts} +2 -2
- package/dist/{thread-manager-CbpiGq1L.d.ts → thread-manager-C-C4pI2z.d.ts} +2 -2
- package/dist/{thread-manager-BBzNgQWH.d.cts → thread-manager-CzYln2OC.d.cts} +2 -2
- package/dist/{thread-manager-DzXm9eeI.d.cts → thread-manager-D4vgzYrh.d.cts} +2 -2
- package/dist/{types-yiXmqedU.d.ts → types-B37hKoWA.d.ts} +1 -1
- package/dist/{types-DQ1l_gXL.d.cts → types-BO7Yju20.d.cts} +63 -14
- package/dist/{types-wiGLvxWf.d.ts → types-CNuWnvy9.d.ts} +1 -1
- package/dist/{types-CADc5V_P.d.ts → types-CPKDl-y_.d.ts} +63 -14
- package/dist/{types-Mc_4BCfT.d.cts → types-D08CXPh8.d.cts} +1 -1
- package/dist/{types-CBH54cwr.d.cts → types-DWEUmYAJ.d.cts} +1 -1
- package/dist/{types-DxCpFNv_.d.cts → types-tQL9njTu.d.cts} +25 -0
- package/dist/{types-DxCpFNv_.d.ts → types-tQL9njTu.d.ts} +25 -0
- package/dist/{workflow-P2pTSfKu.d.ts → workflow-CjXHbZZc.d.ts} +2 -2
- package/dist/{workflow-DhtWRovz.d.cts → workflow-Do_lzJpT.d.cts} +2 -2
- package/dist/workflow.cjs +182 -114
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +3 -3
- package/dist/workflow.d.ts +3 -3
- package/dist/workflow.js +183 -115
- package/dist/workflow.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/sandbox/bedrock/filesystem.ts +6 -12
- package/src/adapters/sandbox/bedrock/index.ts +10 -8
- package/src/adapters/sandbox/bedrock/proxy.ts +2 -0
- package/src/adapters/sandbox/daytona/filesystem.ts +29 -6
- package/src/adapters/sandbox/daytona/index.ts +6 -0
- package/src/adapters/sandbox/daytona/proxy.ts +2 -0
- package/src/adapters/sandbox/e2b/filesystem.ts +5 -4
- package/src/adapters/sandbox/e2b/index.ts +63 -12
- package/src/adapters/sandbox/e2b/proxy.ts +2 -0
- package/src/adapters/sandbox/inmemory/index.ts +5 -0
- package/src/adapters/sandbox/inmemory/proxy.ts +2 -0
- package/src/adapters/thread/anthropic/activities.ts +49 -26
- package/src/adapters/thread/anthropic/model-invoker.ts +15 -6
- package/src/adapters/thread/anthropic/proxy.ts +6 -2
- package/src/adapters/thread/anthropic/thread-manager.test.ts +26 -7
- package/src/adapters/thread/anthropic/thread-manager.ts +60 -46
- package/src/adapters/thread/google-genai/activities.ts +7 -2
- package/src/adapters/thread/google-genai/model-invoker.ts +26 -8
- package/src/adapters/thread/google-genai/proxy.ts +6 -2
- package/src/adapters/thread/google-genai/thread-manager.test.ts +13 -3
- package/src/adapters/thread/google-genai/thread-manager.ts +54 -33
- package/src/adapters/thread/langchain/activities.ts +46 -24
- package/src/adapters/thread/langchain/hooks.test.ts +36 -49
- package/src/adapters/thread/langchain/hooks.ts +18 -5
- package/src/adapters/thread/langchain/model-invoker.ts +3 -3
- package/src/adapters/thread/langchain/proxy.ts +6 -2
- package/src/adapters/thread/langchain/thread-manager.test.ts +5 -1
- package/src/adapters/thread/langchain/thread-manager.ts +20 -9
- package/src/index.ts +4 -1
- package/src/lib/activity.ts +16 -6
- package/src/lib/hooks/types.ts +6 -6
- package/src/lib/lifecycle.ts +9 -1
- package/src/lib/model/proxy.ts +2 -2
- package/src/lib/observability/hooks.ts +4 -5
- package/src/lib/observability/index.ts +1 -4
- package/src/lib/sandbox/manager.ts +21 -4
- package/src/lib/sandbox/node-fs.ts +3 -6
- package/src/lib/sandbox/sandbox.test.ts +36 -3
- package/src/lib/sandbox/tree.integration.test.ts +10 -3
- package/src/lib/sandbox/types.ts +35 -1
- package/src/lib/session/session-edge-cases.integration.test.ts +51 -13
- package/src/lib/session/session.integration.test.ts +139 -0
- package/src/lib/session/session.ts +50 -19
- package/src/lib/session/types.ts +13 -5
- package/src/lib/skills/fs-provider.ts +12 -8
- package/src/lib/skills/handler.ts +1 -1
- package/src/lib/skills/parse.ts +3 -1
- package/src/lib/skills/register.ts +1 -3
- package/src/lib/skills/skills.integration.test.ts +25 -15
- package/src/lib/state/manager.integration.test.ts +12 -2
- package/src/lib/subagent/define.ts +1 -1
- package/src/lib/subagent/handler.ts +186 -71
- package/src/lib/subagent/index.ts +1 -5
- package/src/lib/subagent/register.ts +3 -2
- package/src/lib/subagent/signals.ts +1 -10
- package/src/lib/subagent/subagent.integration.test.ts +438 -156
- package/src/lib/subagent/tool.ts +4 -3
- package/src/lib/subagent/types.ts +50 -20
- package/src/lib/subagent/workflow.ts +9 -49
- package/src/lib/thread/id.test.ts +1 -1
- package/src/lib/thread/id.ts +1 -2
- package/src/lib/thread/proxy.ts +3 -4
- package/src/lib/thread/types.ts +11 -3
- package/src/lib/tool-router/index.ts +1 -5
- package/src/lib/tool-router/router-edge-cases.integration.test.ts +1 -1
- package/src/lib/tool-router/router.ts +3 -2
- package/src/lib/tool-router/types.ts +11 -3
- package/src/lib/tool-router/with-sandbox.ts +19 -5
- package/src/lib/virtual-fs/filesystem.ts +1 -1
- package/src/lib/virtual-fs/index.ts +5 -1
- package/src/lib/virtual-fs/mutations.ts +2 -4
- package/src/lib/virtual-fs/queries.ts +9 -5
- package/src/lib/virtual-fs/types.ts +4 -1
- package/src/lib/virtual-fs/virtual-fs.test.ts +9 -11
- package/src/lib/workflow.test.ts +7 -4
- package/src/lib/workflow.ts +1 -5
- package/src/tools/ask-user-question/tool.ts +1 -3
- package/src/tools/glob/handler.ts +1 -4
- package/src/tools/task-get/handler.ts +4 -5
- package/src/tools/task-list/handler.ts +1 -4
- package/src/tools/task-update/handler.ts +4 -5
- package/src/workflow.ts +20 -7
- package/tsup.config.ts +9 -6
|
@@ -50,8 +50,12 @@ export interface SandboxManagerHooks<
|
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* Called after a sandbox has been successfully created.
|
|
53
|
+
*
|
|
54
|
+
* Receives the live {@link Sandbox} instance so the hook can run setup
|
|
55
|
+
* commands, seed files, or capture identifiers without an extra
|
|
56
|
+
* `provider.get()` round-trip.
|
|
53
57
|
*/
|
|
54
|
-
onPostCreate?: (
|
|
58
|
+
onPostCreate?: (sandbox: Sandbox, ctx: TCtx) => Promise<void>;
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
/**
|
|
@@ -87,8 +91,9 @@ export interface SandboxManagerHooks<
|
|
|
87
91
|
* for (const p of filePaths) files[p] = await db.readFile(projectId, p);
|
|
88
92
|
* return { modifiedOptions: { initialFiles: files } };
|
|
89
93
|
* },
|
|
90
|
-
* onPostCreate: async (
|
|
91
|
-
* console.log("Sandbox created:",
|
|
94
|
+
* onPostCreate: async (sandbox) => {
|
|
95
|
+
* console.log("Sandbox created:", sandbox.id);
|
|
96
|
+
* await sandbox.exec("git init");
|
|
92
97
|
* },
|
|
93
98
|
* },
|
|
94
99
|
* },
|
|
@@ -148,7 +153,7 @@ export class SandboxManager<
|
|
|
148
153
|
const { sandbox } = await this.provider.create(providerOptions);
|
|
149
154
|
|
|
150
155
|
if (this.hooks.onPostCreate) {
|
|
151
|
-
await this.hooks.onPostCreate(sandbox
|
|
156
|
+
await this.hooks.onPostCreate(sandbox, ctx ?? ({} as TCtx));
|
|
152
157
|
}
|
|
153
158
|
|
|
154
159
|
return { sandboxId: sandbox.id };
|
|
@@ -179,6 +184,10 @@ export class SandboxManager<
|
|
|
179
184
|
return sandbox.id;
|
|
180
185
|
}
|
|
181
186
|
|
|
187
|
+
async deleteSnapshot(snapshot: SandboxSnapshot): Promise<void> {
|
|
188
|
+
await this.provider.deleteSnapshot(snapshot);
|
|
189
|
+
}
|
|
190
|
+
|
|
182
191
|
async fork(sandboxId: string): Promise<string> {
|
|
183
192
|
const sandbox = await this.provider.fork(sandboxId);
|
|
184
193
|
return sandbox.id;
|
|
@@ -232,6 +241,14 @@ export class SandboxManager<
|
|
|
232
241
|
snapshotSandbox: async (sandboxId: string): Promise<SandboxSnapshot> => {
|
|
233
242
|
return this.snapshot(sandboxId);
|
|
234
243
|
},
|
|
244
|
+
restoreSandbox: async (snapshot: SandboxSnapshot): Promise<string> => {
|
|
245
|
+
return this.restore(snapshot);
|
|
246
|
+
},
|
|
247
|
+
deleteSandboxSnapshot: async (
|
|
248
|
+
snapshot: SandboxSnapshot
|
|
249
|
+
): Promise<void> => {
|
|
250
|
+
await this.deleteSnapshot(snapshot);
|
|
251
|
+
},
|
|
235
252
|
forkSandbox: async (sandboxId: string): Promise<string> => {
|
|
236
253
|
return this.fork(sandboxId);
|
|
237
254
|
},
|
|
@@ -65,10 +65,7 @@ export class NodeFsSandboxFileSystem implements SandboxFileSystem {
|
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
async mkdir(
|
|
69
|
-
path: string,
|
|
70
|
-
options?: { recursive?: boolean },
|
|
71
|
-
): Promise<void> {
|
|
68
|
+
async mkdir(path: string, options?: { recursive?: boolean }): Promise<void> {
|
|
72
69
|
await fsp.mkdir(this.abs(path), options);
|
|
73
70
|
}
|
|
74
71
|
|
|
@@ -88,7 +85,7 @@ export class NodeFsSandboxFileSystem implements SandboxFileSystem {
|
|
|
88
85
|
|
|
89
86
|
async rm(
|
|
90
87
|
path: string,
|
|
91
|
-
options?: { recursive?: boolean; force?: boolean }
|
|
88
|
+
options?: { recursive?: boolean; force?: boolean }
|
|
92
89
|
): Promise<void> {
|
|
93
90
|
await fsp.rm(this.abs(path), options);
|
|
94
91
|
}
|
|
@@ -96,7 +93,7 @@ export class NodeFsSandboxFileSystem implements SandboxFileSystem {
|
|
|
96
93
|
async cp(
|
|
97
94
|
src: string,
|
|
98
95
|
dest: string,
|
|
99
|
-
options?: { recursive?: boolean }
|
|
96
|
+
options?: { recursive?: boolean }
|
|
100
97
|
): Promise<void> {
|
|
101
98
|
await fsp.cp(this.abs(src), this.abs(dest), options);
|
|
102
99
|
}
|
|
@@ -157,18 +157,21 @@ describe("SandboxManager", () => {
|
|
|
157
157
|
expect(await sandbox.fs.readFile("/file.txt")).toBe("explicit");
|
|
158
158
|
});
|
|
159
159
|
|
|
160
|
-
it("onPostCreate hook receives
|
|
160
|
+
it("onPostCreate hook receives the live sandbox", async () => {
|
|
161
161
|
let capturedId: string | undefined;
|
|
162
162
|
const mgr = new SandboxManager(new InMemorySandboxProvider(), {
|
|
163
163
|
hooks: {
|
|
164
|
-
onPostCreate: async (
|
|
165
|
-
capturedId =
|
|
164
|
+
onPostCreate: async (sandbox) => {
|
|
165
|
+
capturedId = sandbox.id;
|
|
166
|
+
await sandbox.fs.writeFile("/hook.txt", "written-by-hook");
|
|
166
167
|
},
|
|
167
168
|
},
|
|
168
169
|
});
|
|
169
170
|
|
|
170
171
|
const { sandboxId } = await mustCreate(mgr);
|
|
171
172
|
expect(capturedId).toBe(sandboxId);
|
|
173
|
+
const sandbox = await mgr.getSandbox(sandboxId);
|
|
174
|
+
expect(await sandbox.fs.readFile("/hook.txt")).toBe("written-by-hook");
|
|
172
175
|
});
|
|
173
176
|
|
|
174
177
|
it("onPostCreate hook does not run when creation is skipped", async () => {
|
|
@@ -192,6 +195,8 @@ describe("SandboxManager", () => {
|
|
|
192
195
|
expect(activities.inMemoryTestCreateSandbox).toBeTypeOf("function");
|
|
193
196
|
expect(activities.inMemoryTestDestroySandbox).toBeTypeOf("function");
|
|
194
197
|
expect(activities.inMemoryTestSnapshotSandbox).toBeTypeOf("function");
|
|
198
|
+
expect(activities.inMemoryTestRestoreSandbox).toBeTypeOf("function");
|
|
199
|
+
expect(activities.inMemoryTestDeleteSandboxSnapshot).toBeTypeOf("function");
|
|
195
200
|
|
|
196
201
|
const result = await activities.inMemoryTestCreateSandbox();
|
|
197
202
|
expect(result).not.toBeNull();
|
|
@@ -203,6 +208,34 @@ describe("SandboxManager", () => {
|
|
|
203
208
|
SandboxNotFoundError
|
|
204
209
|
);
|
|
205
210
|
});
|
|
211
|
+
|
|
212
|
+
it("restoreSandbox activity creates a new sandbox from a snapshot", async () => {
|
|
213
|
+
const activities = manager.createActivities("Test");
|
|
214
|
+
|
|
215
|
+
const created = await activities.inMemoryTestCreateSandbox({
|
|
216
|
+
initialFiles: { "/greeting.txt": "hello" },
|
|
217
|
+
});
|
|
218
|
+
const { sandboxId } = created as { sandboxId: string };
|
|
219
|
+
|
|
220
|
+
const snapshot = await activities.inMemoryTestSnapshotSandbox(sandboxId);
|
|
221
|
+
await activities.inMemoryTestDestroySandbox(sandboxId);
|
|
222
|
+
|
|
223
|
+
const restoredId = await activities.inMemoryTestRestoreSandbox(snapshot);
|
|
224
|
+
const restored = await manager.getSandbox(restoredId);
|
|
225
|
+
expect(await restored.fs.readFile("/greeting.txt")).toBe("hello");
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("deleteSandboxSnapshot activity is a no-op for in-memory snapshots", async () => {
|
|
229
|
+
const activities = manager.createActivities("Test");
|
|
230
|
+
|
|
231
|
+
const created = await activities.inMemoryTestCreateSandbox();
|
|
232
|
+
const { sandboxId } = created as { sandboxId: string };
|
|
233
|
+
|
|
234
|
+
const snapshot = await activities.inMemoryTestSnapshotSandbox(sandboxId);
|
|
235
|
+
await expect(
|
|
236
|
+
activities.inMemoryTestDeleteSandboxSnapshot(snapshot)
|
|
237
|
+
).resolves.toBeUndefined();
|
|
238
|
+
});
|
|
206
239
|
});
|
|
207
240
|
|
|
208
241
|
describe("InMemorySandboxProvider", () => {
|
|
@@ -3,7 +3,10 @@ import { toTree } from "./tree";
|
|
|
3
3
|
import type { SandboxFileSystem, DirentEntry } from "./types";
|
|
4
4
|
|
|
5
5
|
function createMockFs(
|
|
6
|
-
structure: Record<
|
|
6
|
+
structure: Record<
|
|
7
|
+
string,
|
|
8
|
+
{ isDir: boolean; isLink?: boolean; linkTarget?: string }
|
|
9
|
+
>
|
|
7
10
|
): SandboxFileSystem {
|
|
8
11
|
return {
|
|
9
12
|
workspaceBase: "/",
|
|
@@ -21,13 +24,17 @@ function createMockFs(
|
|
|
21
24
|
readdir: async (dir: string) => {
|
|
22
25
|
const prefix = dir.endsWith("/") ? dir : dir + "/";
|
|
23
26
|
return Object.keys(structure)
|
|
24
|
-
.filter(
|
|
27
|
+
.filter(
|
|
28
|
+
(p) => p.startsWith(prefix) && !p.slice(prefix.length).includes("/")
|
|
29
|
+
)
|
|
25
30
|
.map((p) => p.slice(prefix.length));
|
|
26
31
|
},
|
|
27
32
|
readdirWithFileTypes: async (dir: string): Promise<DirentEntry[]> => {
|
|
28
33
|
const prefix = dir.endsWith("/") ? dir : dir + "/";
|
|
29
34
|
return Object.entries(structure)
|
|
30
|
-
.filter(
|
|
35
|
+
.filter(
|
|
36
|
+
([p]) => p.startsWith(prefix) && !p.slice(prefix.length).includes("/")
|
|
37
|
+
)
|
|
31
38
|
.map(([p, meta]) => ({
|
|
32
39
|
name: p.slice(prefix.length),
|
|
33
40
|
isFile: !meta.isDir && !meta.isLink,
|
package/src/lib/sandbox/types.ts
CHANGED
|
@@ -17,6 +17,21 @@ export interface FileStat {
|
|
|
17
17
|
mtime: Date;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Network & lifecycle
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
export interface SandboxNetworkConfig {
|
|
25
|
+
allowOut?: string[];
|
|
26
|
+
denyOut?: string[];
|
|
27
|
+
allowPublicTraffic?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface SandboxLifecycleConfig {
|
|
31
|
+
onTimeout: "kill" | "pause";
|
|
32
|
+
autoResume?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
20
35
|
/**
|
|
21
36
|
* Provider-agnostic filesystem interface.
|
|
22
37
|
*
|
|
@@ -114,6 +129,16 @@ export interface SandboxCreateOptions {
|
|
|
114
129
|
initialFiles?: Record<string, string | Uint8Array>;
|
|
115
130
|
/** Environment variables available inside the sandbox */
|
|
116
131
|
env?: Record<string, string>;
|
|
132
|
+
/** Key-value metadata surfaced via provider list/query APIs */
|
|
133
|
+
metadata?: Record<string, string>;
|
|
134
|
+
/** Sandbox idle timeout in milliseconds */
|
|
135
|
+
timeoutMs?: number;
|
|
136
|
+
/** Enable or disable outbound internet access */
|
|
137
|
+
allowInternetAccess?: boolean;
|
|
138
|
+
/** Outbound network allow/deny rules */
|
|
139
|
+
network?: SandboxNetworkConfig;
|
|
140
|
+
/** Sandbox timeout behaviour */
|
|
141
|
+
lifecycle?: SandboxLifecycleConfig;
|
|
117
142
|
}
|
|
118
143
|
|
|
119
144
|
export interface SandboxCreateResult {
|
|
@@ -135,6 +160,8 @@ export interface SandboxProvider<
|
|
|
135
160
|
resume(sandboxId: string): Promise<void>;
|
|
136
161
|
snapshot(sandboxId: string): Promise<SandboxSnapshot>;
|
|
137
162
|
restore(snapshot: SandboxSnapshot): Promise<Sandbox>;
|
|
163
|
+
/** Delete a previously captured snapshot. No-op if already deleted. */
|
|
164
|
+
deleteSnapshot(snapshot: SandboxSnapshot): Promise<void>;
|
|
138
165
|
fork(sandboxId: string): Promise<Sandbox>;
|
|
139
166
|
}
|
|
140
167
|
|
|
@@ -155,6 +182,10 @@ export interface SandboxOps<
|
|
|
155
182
|
/** Resume a paused sandbox. No-op if already running. */
|
|
156
183
|
resumeSandbox(sandboxId: string): Promise<void>;
|
|
157
184
|
snapshotSandbox(sandboxId: string): Promise<SandboxSnapshot>;
|
|
185
|
+
/** Create a fresh sandbox from a previously captured snapshot. */
|
|
186
|
+
restoreSandbox(snapshot: SandboxSnapshot): Promise<string>;
|
|
187
|
+
/** Delete a previously captured snapshot. No-op if already deleted. */
|
|
188
|
+
deleteSandboxSnapshot(snapshot: SandboxSnapshot): Promise<void>;
|
|
158
189
|
forkSandbox(sandboxId: string): Promise<string>;
|
|
159
190
|
}
|
|
160
191
|
|
|
@@ -172,7 +203,10 @@ export type PrefixedSandboxOps<
|
|
|
172
203
|
TOptions extends SandboxCreateOptions = SandboxCreateOptions,
|
|
173
204
|
TCtx = unknown,
|
|
174
205
|
> = {
|
|
175
|
-
[K in keyof SandboxOps<
|
|
206
|
+
[K in keyof SandboxOps<
|
|
207
|
+
TOptions,
|
|
208
|
+
TCtx
|
|
209
|
+
> as `${TPrefix}${Capitalize<K & string>}`]: SandboxOps<TOptions, TCtx>[K];
|
|
176
210
|
};
|
|
177
211
|
|
|
178
212
|
// ============================================================================
|
|
@@ -42,7 +42,13 @@ vi.mock("@temporalio/workflow", () => {
|
|
|
42
42
|
uuid4: () =>
|
|
43
43
|
`00000000-0000-0000-0000-${String(++idCounter).padStart(12, "0")}`,
|
|
44
44
|
ApplicationFailure: MockApplicationFailure,
|
|
45
|
-
log: {
|
|
45
|
+
log: {
|
|
46
|
+
trace: () => {},
|
|
47
|
+
debug: () => {},
|
|
48
|
+
info: () => {},
|
|
49
|
+
warn: () => {},
|
|
50
|
+
error: () => {},
|
|
51
|
+
},
|
|
46
52
|
};
|
|
47
53
|
});
|
|
48
54
|
|
|
@@ -61,7 +67,9 @@ type TurnScript = {
|
|
|
61
67
|
* Wraps every method on a ThreadOps object so it also has `.executeWithOptions()`,
|
|
62
68
|
* matching Temporal's `ActivityInterfaceFor<ThreadOps>` shape.
|
|
63
69
|
*/
|
|
64
|
-
function toActivityInterface<TContent = string>(
|
|
70
|
+
function toActivityInterface<TContent = string>(
|
|
71
|
+
raw: ThreadOps<TContent>
|
|
72
|
+
): ActivityInterfaceFor<ThreadOps<TContent>> {
|
|
65
73
|
const result = {} as Record<string, unknown>;
|
|
66
74
|
for (const [key, fn] of Object.entries(raw)) {
|
|
67
75
|
const wrapped = (...args: unknown[]) =>
|
|
@@ -433,6 +441,8 @@ describe("createSession edge cases", () => {
|
|
|
433
441
|
createdAt: new Date().toISOString(),
|
|
434
442
|
}),
|
|
435
443
|
forkSandbox: async () => "forked-sandbox-id",
|
|
444
|
+
restoreSandbox: async () => "restored-sandbox-id",
|
|
445
|
+
deleteSandboxSnapshot: async () => {},
|
|
436
446
|
pauseSandbox: async () => {},
|
|
437
447
|
resumeSandbox: async () => {},
|
|
438
448
|
};
|
|
@@ -476,6 +486,8 @@ describe("createSession edge cases", () => {
|
|
|
476
486
|
createdAt: new Date().toISOString(),
|
|
477
487
|
}),
|
|
478
488
|
forkSandbox: async () => "forked-sandbox-id",
|
|
489
|
+
restoreSandbox: async () => "restored-sandbox-id",
|
|
490
|
+
deleteSandboxSnapshot: async () => {},
|
|
479
491
|
pauseSandbox: async () => {},
|
|
480
492
|
resumeSandbox: async () => {},
|
|
481
493
|
};
|
|
@@ -765,7 +777,11 @@ describe("createSession edge cases", () => {
|
|
|
765
777
|
},
|
|
766
778
|
});
|
|
767
779
|
|
|
768
|
-
const session = await createSession<
|
|
780
|
+
const session = await createSession<
|
|
781
|
+
Record<string, never>,
|
|
782
|
+
unknown,
|
|
783
|
+
TestContent
|
|
784
|
+
>({
|
|
769
785
|
agentName: "TestAgent",
|
|
770
786
|
thread: { mode: "new", threadId: "thread-1" },
|
|
771
787
|
runAgent: createScriptedRunAgent([{ message: "done", toolCalls: [] }]),
|
|
@@ -940,6 +956,8 @@ describe("createSession edge cases", () => {
|
|
|
940
956
|
destroySandbox: async () => {},
|
|
941
957
|
snapshotSandbox: snapshotSpy,
|
|
942
958
|
forkSandbox: async () => "forked-sandbox-id",
|
|
959
|
+
restoreSandbox: async () => "restored-sandbox-id",
|
|
960
|
+
deleteSandboxSnapshot: async () => {},
|
|
943
961
|
pauseSandbox: async () => {},
|
|
944
962
|
resumeSandbox: async () => {},
|
|
945
963
|
};
|
|
@@ -1040,6 +1058,8 @@ describe("createSession edge cases", () => {
|
|
|
1040
1058
|
createdAt: new Date().toISOString(),
|
|
1041
1059
|
}),
|
|
1042
1060
|
forkSandbox: async () => "forked-sb",
|
|
1061
|
+
restoreSandbox: async () => "restored-sb",
|
|
1062
|
+
deleteSandboxSnapshot: async () => {},
|
|
1043
1063
|
};
|
|
1044
1064
|
|
|
1045
1065
|
const session = await createSession({
|
|
@@ -1074,6 +1094,8 @@ describe("createSession edge cases", () => {
|
|
|
1074
1094
|
createdAt: new Date().toISOString(),
|
|
1075
1095
|
}),
|
|
1076
1096
|
forkSandbox: async () => "forked-sb",
|
|
1097
|
+
restoreSandbox: async () => "restored-sb",
|
|
1098
|
+
deleteSandboxSnapshot: async () => {},
|
|
1077
1099
|
};
|
|
1078
1100
|
|
|
1079
1101
|
const session = await createSession({
|
|
@@ -1119,6 +1141,8 @@ describe("createSession edge cases", () => {
|
|
|
1119
1141
|
createdAt: new Date().toISOString(),
|
|
1120
1142
|
}),
|
|
1121
1143
|
forkSandbox: async () => "forked-sb",
|
|
1144
|
+
restoreSandbox: async () => "restored-sb",
|
|
1145
|
+
deleteSandboxSnapshot: async () => {},
|
|
1122
1146
|
};
|
|
1123
1147
|
|
|
1124
1148
|
const session = await createSession({
|
|
@@ -1166,6 +1190,8 @@ describe("createSession edge cases", () => {
|
|
|
1166
1190
|
sandboxLog.push(`fork:${id}`);
|
|
1167
1191
|
return `forked-from-${id}`;
|
|
1168
1192
|
},
|
|
1193
|
+
restoreSandbox: async () => "restored-sb",
|
|
1194
|
+
deleteSandboxSnapshot: async () => {},
|
|
1169
1195
|
};
|
|
1170
1196
|
|
|
1171
1197
|
const session = await createSession({
|
|
@@ -1186,7 +1212,9 @@ describe("createSession edge cases", () => {
|
|
|
1186
1212
|
|
|
1187
1213
|
expect(sandboxLog).toContain("fork:paused-sb-1");
|
|
1188
1214
|
expect(sandboxLog).not.toContain("create");
|
|
1189
|
-
expect((result as { sandboxId?: string }).sandboxId).toBe(
|
|
1215
|
+
expect((result as { sandboxId?: string }).sandboxId).toBe(
|
|
1216
|
+
"forked-from-paused-sb-1"
|
|
1217
|
+
);
|
|
1190
1218
|
expect(sandboxLog).toContain("destroy:forked-from-paused-sb-1");
|
|
1191
1219
|
});
|
|
1192
1220
|
|
|
@@ -1210,6 +1238,8 @@ describe("createSession edge cases", () => {
|
|
|
1210
1238
|
createdAt: new Date().toISOString(),
|
|
1211
1239
|
}),
|
|
1212
1240
|
forkSandbox: async () => "forked-sb",
|
|
1241
|
+
restoreSandbox: async () => "restored-sb",
|
|
1242
|
+
deleteSandboxSnapshot: async () => {},
|
|
1213
1243
|
};
|
|
1214
1244
|
|
|
1215
1245
|
const session = await createSession({
|
|
@@ -1253,6 +1283,8 @@ describe("createSession edge cases", () => {
|
|
|
1253
1283
|
createdAt: new Date().toISOString(),
|
|
1254
1284
|
}),
|
|
1255
1285
|
forkSandbox: async () => "forked-sb",
|
|
1286
|
+
restoreSandbox: async () => "restored-sb",
|
|
1287
|
+
deleteSandboxSnapshot: async () => {},
|
|
1256
1288
|
};
|
|
1257
1289
|
|
|
1258
1290
|
const session = await createSession({
|
|
@@ -1297,6 +1329,8 @@ describe("createSession edge cases", () => {
|
|
|
1297
1329
|
createdAt: new Date().toISOString(),
|
|
1298
1330
|
}),
|
|
1299
1331
|
forkSandbox: async () => "forked-sb",
|
|
1332
|
+
restoreSandbox: async () => "restored-sb",
|
|
1333
|
+
deleteSandboxSnapshot: async () => {},
|
|
1300
1334
|
};
|
|
1301
1335
|
|
|
1302
1336
|
const session = await createSession({
|
|
@@ -1370,9 +1404,7 @@ describe("createSession edge cases", () => {
|
|
|
1370
1404
|
|
|
1371
1405
|
const session = await createSession({
|
|
1372
1406
|
agentName: "TestAgent",
|
|
1373
|
-
runAgent: createScriptedRunAgent([
|
|
1374
|
-
{ message: "done", toolCalls: [] },
|
|
1375
|
-
]),
|
|
1407
|
+
runAgent: createScriptedRunAgent([{ message: "done", toolCalls: [] }]),
|
|
1376
1408
|
threadOps: ops,
|
|
1377
1409
|
buildContextMessage: () => "go",
|
|
1378
1410
|
});
|
|
@@ -1413,6 +1445,8 @@ describe("createSession edge cases", () => {
|
|
|
1413
1445
|
createdAt: new Date().toISOString(),
|
|
1414
1446
|
}),
|
|
1415
1447
|
forkSandbox: async () => "forked-sb",
|
|
1448
|
+
restoreSandbox: async () => "restored-sb",
|
|
1449
|
+
deleteSandboxSnapshot: async () => {},
|
|
1416
1450
|
};
|
|
1417
1451
|
|
|
1418
1452
|
const session = await createSession({
|
|
@@ -1431,9 +1465,7 @@ describe("createSession edge cases", () => {
|
|
|
1431
1465
|
initialState: { systemPrompt: "test" },
|
|
1432
1466
|
});
|
|
1433
1467
|
|
|
1434
|
-
await expect(session.runSession({ stateManager })).rejects.toThrow(
|
|
1435
|
-
"crash"
|
|
1436
|
-
);
|
|
1468
|
+
await expect(session.runSession({ stateManager })).rejects.toThrow("crash");
|
|
1437
1469
|
|
|
1438
1470
|
expect(sandboxLog).toContain("pause:sb-err");
|
|
1439
1471
|
expect(sandboxLog).not.toContain("destroy:sb-err");
|
|
@@ -1461,6 +1493,8 @@ describe("createSession edge cases", () => {
|
|
|
1461
1493
|
createdAt: new Date().toISOString(),
|
|
1462
1494
|
}),
|
|
1463
1495
|
forkSandbox: async () => "forked-sb",
|
|
1496
|
+
restoreSandbox: async () => "restored-sb",
|
|
1497
|
+
deleteSandboxSnapshot: async () => {},
|
|
1464
1498
|
};
|
|
1465
1499
|
|
|
1466
1500
|
const session = await createSession({
|
|
@@ -1503,6 +1537,8 @@ describe("createSession edge cases", () => {
|
|
|
1503
1537
|
createdAt: new Date().toISOString(),
|
|
1504
1538
|
}),
|
|
1505
1539
|
forkSandbox: async () => "forked-sb",
|
|
1540
|
+
restoreSandbox: async () => "restored-sb",
|
|
1541
|
+
deleteSandboxSnapshot: async () => {},
|
|
1506
1542
|
};
|
|
1507
1543
|
|
|
1508
1544
|
const session = await createSession({
|
|
@@ -1521,9 +1557,7 @@ describe("createSession edge cases", () => {
|
|
|
1521
1557
|
initialState: { systemPrompt: "test" },
|
|
1522
1558
|
});
|
|
1523
1559
|
|
|
1524
|
-
await expect(session.runSession({ stateManager })).rejects.toThrow(
|
|
1525
|
-
"crash"
|
|
1526
|
-
);
|
|
1560
|
+
await expect(session.runSession({ stateManager })).rejects.toThrow("crash");
|
|
1527
1561
|
|
|
1528
1562
|
expect(sandboxLog).not.toContain("pause:sb-keep-err");
|
|
1529
1563
|
expect(sandboxLog).not.toContain("destroy:sb-keep-err");
|
|
@@ -1553,6 +1587,8 @@ describe("createSession edge cases", () => {
|
|
|
1553
1587
|
createdAt: new Date().toISOString(),
|
|
1554
1588
|
}),
|
|
1555
1589
|
forkSandbox: async () => "forked-sb",
|
|
1590
|
+
restoreSandbox: async () => "restored-sb",
|
|
1591
|
+
deleteSandboxSnapshot: async () => {},
|
|
1556
1592
|
};
|
|
1557
1593
|
|
|
1558
1594
|
const session = await createSession({
|
|
@@ -1598,6 +1634,8 @@ describe("createSession edge cases", () => {
|
|
|
1598
1634
|
createdAt: new Date().toISOString(),
|
|
1599
1635
|
}),
|
|
1600
1636
|
forkSandbox: async () => "forked-sb",
|
|
1637
|
+
restoreSandbox: async () => "restored-sb",
|
|
1638
|
+
deleteSandboxSnapshot: async () => {},
|
|
1601
1639
|
};
|
|
1602
1640
|
|
|
1603
1641
|
const session = await createSession({
|
|
@@ -517,6 +517,8 @@ describe("createSession integration", () => {
|
|
|
517
517
|
createdAt: new Date().toISOString(),
|
|
518
518
|
}),
|
|
519
519
|
forkSandbox: async () => "forked-sandbox-id",
|
|
520
|
+
restoreSandbox: async () => "restored-sandbox-id",
|
|
521
|
+
deleteSandboxSnapshot: async () => {},
|
|
520
522
|
pauseSandbox: async () => {},
|
|
521
523
|
resumeSandbox: async () => {},
|
|
522
524
|
};
|
|
@@ -559,6 +561,8 @@ describe("createSession integration", () => {
|
|
|
559
561
|
createdAt: new Date().toISOString(),
|
|
560
562
|
}),
|
|
561
563
|
forkSandbox: async () => "forked-sandbox-id",
|
|
564
|
+
restoreSandbox: async () => "restored-sandbox-id",
|
|
565
|
+
deleteSandboxSnapshot: async () => {},
|
|
562
566
|
pauseSandbox: async () => {},
|
|
563
567
|
resumeSandbox: async () => {},
|
|
564
568
|
};
|
|
@@ -610,6 +614,8 @@ describe("createSession integration", () => {
|
|
|
610
614
|
createdAt: new Date().toISOString(),
|
|
611
615
|
}),
|
|
612
616
|
forkSandbox: async () => "forked-sb",
|
|
617
|
+
restoreSandbox: async () => "restored-sb",
|
|
618
|
+
deleteSandboxSnapshot: async () => {},
|
|
613
619
|
};
|
|
614
620
|
|
|
615
621
|
const session = await createSession({
|
|
@@ -822,6 +828,8 @@ describe("createSession integration", () => {
|
|
|
822
828
|
createdAt: new Date().toISOString(),
|
|
823
829
|
}),
|
|
824
830
|
forkSandbox: async () => "forked-sandbox-id",
|
|
831
|
+
restoreSandbox: async () => "restored-sandbox-id",
|
|
832
|
+
deleteSandboxSnapshot: async () => {},
|
|
825
833
|
pauseSandbox: async () => {},
|
|
826
834
|
resumeSandbox: async () => {},
|
|
827
835
|
};
|
|
@@ -873,6 +881,8 @@ describe("createSession integration", () => {
|
|
|
873
881
|
createdAt: new Date().toISOString(),
|
|
874
882
|
}),
|
|
875
883
|
forkSandbox: async () => "forked-sandbox-id",
|
|
884
|
+
restoreSandbox: async () => "restored-sandbox-id",
|
|
885
|
+
deleteSandboxSnapshot: async () => {},
|
|
876
886
|
pauseSandbox: async () => {},
|
|
877
887
|
resumeSandbox: async () => {},
|
|
878
888
|
};
|
|
@@ -949,4 +959,133 @@ describe("createSession integration", () => {
|
|
|
949
959
|
expect(result.usage.totalInputTokens).toBe(180);
|
|
950
960
|
expect(result.usage.totalOutputTokens).toBe(90);
|
|
951
961
|
});
|
|
962
|
+
|
|
963
|
+
// --- Snapshot-driven shutdown ---
|
|
964
|
+
|
|
965
|
+
it("captures base + exit snapshot and destroys sandbox on sandboxShutdown=snapshot", async () => {
|
|
966
|
+
const { ops } = createMockThreadOps();
|
|
967
|
+
const sandboxLog: string[] = [];
|
|
968
|
+
let snapCounter = 0;
|
|
969
|
+
|
|
970
|
+
const sandboxOps: SandboxOps = {
|
|
971
|
+
createSandbox: async () => {
|
|
972
|
+
sandboxLog.push("create");
|
|
973
|
+
return { sandboxId: "sb-snap" };
|
|
974
|
+
},
|
|
975
|
+
destroySandbox: async (id: string) => {
|
|
976
|
+
sandboxLog.push(`destroy:${id}`);
|
|
977
|
+
},
|
|
978
|
+
pauseSandbox: async () => {
|
|
979
|
+
sandboxLog.push("pause");
|
|
980
|
+
},
|
|
981
|
+
resumeSandbox: async () => {},
|
|
982
|
+
snapshotSandbox: async (id: string) => {
|
|
983
|
+
snapCounter += 1;
|
|
984
|
+
sandboxLog.push(`snapshot:${id}`);
|
|
985
|
+
return {
|
|
986
|
+
sandboxId: id,
|
|
987
|
+
providerId: "test",
|
|
988
|
+
data: { tag: `snap-${snapCounter}` },
|
|
989
|
+
createdAt: new Date().toISOString(),
|
|
990
|
+
};
|
|
991
|
+
},
|
|
992
|
+
restoreSandbox: async () => "restored-sb",
|
|
993
|
+
deleteSandboxSnapshot: async () => {},
|
|
994
|
+
forkSandbox: async () => "forked-sb",
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
const session = await createSession({
|
|
998
|
+
agentName: "TestAgent",
|
|
999
|
+
thread: { mode: "new", threadId: "thread-snap" },
|
|
1000
|
+
runAgent: createScriptedRunAgent([{ message: "done", toolCalls: [] }]),
|
|
1001
|
+
threadOps: ops,
|
|
1002
|
+
buildContextMessage: () => "go",
|
|
1003
|
+
sandboxOps,
|
|
1004
|
+
sandboxShutdown: "snapshot",
|
|
1005
|
+
});
|
|
1006
|
+
|
|
1007
|
+
const stateManager = createAgentStateManager({
|
|
1008
|
+
initialState: { systemPrompt: "test" },
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
const result = await session.runSession({ stateManager });
|
|
1012
|
+
|
|
1013
|
+
expect(result.exitReason).toBe("completed");
|
|
1014
|
+
expect(result.sandboxId).toBe("sb-snap");
|
|
1015
|
+
expect(result.baseSnapshot?.data).toEqual({ tag: "snap-1" });
|
|
1016
|
+
expect(result.snapshot?.data).toEqual({ tag: "snap-2" });
|
|
1017
|
+
expect(sandboxLog).toEqual([
|
|
1018
|
+
"create",
|
|
1019
|
+
"snapshot:sb-snap",
|
|
1020
|
+
"snapshot:sb-snap",
|
|
1021
|
+
"destroy:sb-snap",
|
|
1022
|
+
]);
|
|
1023
|
+
expect(sandboxLog).not.toContain("pause");
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
it("restores a sandbox when sandbox.mode=from-snapshot and skips base snapshot", async () => {
|
|
1027
|
+
const { ops } = createMockThreadOps();
|
|
1028
|
+
const sandboxLog: string[] = [];
|
|
1029
|
+
const priorSnapshot = {
|
|
1030
|
+
sandboxId: "sb-prior",
|
|
1031
|
+
providerId: "test",
|
|
1032
|
+
data: { tag: "prior" },
|
|
1033
|
+
createdAt: new Date().toISOString(),
|
|
1034
|
+
};
|
|
1035
|
+
|
|
1036
|
+
const sandboxOps: SandboxOps = {
|
|
1037
|
+
createSandbox: async () => {
|
|
1038
|
+
sandboxLog.push("create");
|
|
1039
|
+
return { sandboxId: "sb-should-not-be-created" };
|
|
1040
|
+
},
|
|
1041
|
+
destroySandbox: async (id: string) => {
|
|
1042
|
+
sandboxLog.push(`destroy:${id}`);
|
|
1043
|
+
},
|
|
1044
|
+
pauseSandbox: async () => {},
|
|
1045
|
+
resumeSandbox: async () => {},
|
|
1046
|
+
snapshotSandbox: async (id: string) => {
|
|
1047
|
+
sandboxLog.push(`snapshot:${id}`);
|
|
1048
|
+
return {
|
|
1049
|
+
sandboxId: id,
|
|
1050
|
+
providerId: "test",
|
|
1051
|
+
data: { tag: "exit" },
|
|
1052
|
+
createdAt: new Date().toISOString(),
|
|
1053
|
+
};
|
|
1054
|
+
},
|
|
1055
|
+
restoreSandbox: async (snap) => {
|
|
1056
|
+
sandboxLog.push(`restore:${(snap.data as { tag: string }).tag}`);
|
|
1057
|
+
return "sb-restored";
|
|
1058
|
+
},
|
|
1059
|
+
deleteSandboxSnapshot: async () => {},
|
|
1060
|
+
forkSandbox: async () => "forked-sb",
|
|
1061
|
+
};
|
|
1062
|
+
|
|
1063
|
+
const session = await createSession({
|
|
1064
|
+
agentName: "TestAgent",
|
|
1065
|
+
thread: { mode: "new", threadId: "thread-restore" },
|
|
1066
|
+
runAgent: createScriptedRunAgent([{ message: "done", toolCalls: [] }]),
|
|
1067
|
+
threadOps: ops,
|
|
1068
|
+
buildContextMessage: () => "go",
|
|
1069
|
+
sandboxOps,
|
|
1070
|
+
sandbox: { mode: "from-snapshot", snapshot: priorSnapshot },
|
|
1071
|
+
sandboxShutdown: "snapshot",
|
|
1072
|
+
});
|
|
1073
|
+
|
|
1074
|
+
const stateManager = createAgentStateManager({
|
|
1075
|
+
initialState: { systemPrompt: "test" },
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
const result = await session.runSession({ stateManager });
|
|
1079
|
+
|
|
1080
|
+
expect(result.sandboxId).toBe("sb-restored");
|
|
1081
|
+
// No base snapshot because the sandbox was restored, not freshly created.
|
|
1082
|
+
expect(result.baseSnapshot).toBeUndefined();
|
|
1083
|
+
expect(result.snapshot?.data).toEqual({ tag: "exit" });
|
|
1084
|
+
expect(sandboxLog).toEqual([
|
|
1085
|
+
"restore:prior",
|
|
1086
|
+
"snapshot:sb-restored",
|
|
1087
|
+
"destroy:sb-restored",
|
|
1088
|
+
]);
|
|
1089
|
+
expect(sandboxLog).not.toContain("create");
|
|
1090
|
+
});
|
|
952
1091
|
});
|