zeitlich 0.2.15 → 0.2.16
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 +50 -0
- package/dist/adapters/sandbox/daytona/index.cjs +52 -23
- package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
- package/dist/adapters/sandbox/daytona/index.d.cts +10 -2
- package/dist/adapters/sandbox/daytona/index.d.ts +10 -2
- package/dist/adapters/sandbox/daytona/index.js +52 -23
- package/dist/adapters/sandbox/daytona/index.js.map +1 -1
- package/dist/adapters/sandbox/inmemory/index.cjs +21 -16
- package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
- package/dist/adapters/sandbox/inmemory/index.d.cts +1 -1
- package/dist/adapters/sandbox/inmemory/index.d.ts +1 -1
- package/dist/adapters/sandbox/inmemory/index.js +21 -16
- package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
- package/dist/adapters/sandbox/virtual/index.cjs +38 -38
- package/dist/adapters/sandbox/virtual/index.cjs.map +1 -1
- package/dist/adapters/sandbox/virtual/index.d.cts +6 -6
- package/dist/adapters/sandbox/virtual/index.d.ts +6 -6
- package/dist/adapters/sandbox/virtual/index.js +37 -37
- package/dist/adapters/sandbox/virtual/index.js.map +1 -1
- package/dist/adapters/thread/google-genai/index.d.cts +2 -2
- package/dist/adapters/thread/google-genai/index.d.ts +2 -2
- package/dist/adapters/thread/langchain/index.d.cts +2 -2
- package/dist/adapters/thread/langchain/index.d.ts +2 -2
- package/dist/index.cjs +2 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/{types-CDubRtad.d.cts → types-BMRzfELQ.d.cts} +2 -0
- package/dist/{types-CDubRtad.d.ts → types-BMRzfELQ.d.ts} +2 -0
- package/dist/{types-CwwgQ_9H.d.ts → types-BSOte_8s.d.ts} +6 -2
- package/dist/{types-BVP87m_W.d.cts → types-DCi2qXjN.d.cts} +6 -2
- package/dist/{types-Dje1TdH6.d.cts → types-Drli9aCK.d.cts} +1 -1
- package/dist/{types-BWvIYK28.d.ts → types-XPtivmSJ.d.ts} +1 -1
- package/dist/workflow.cjs +2 -3
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +6 -6
- package/dist/workflow.d.ts +6 -6
- package/dist/workflow.js +2 -3
- package/dist/workflow.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/sandbox/daytona/filesystem.ts +43 -19
- package/src/adapters/sandbox/daytona/index.ts +16 -3
- package/src/adapters/sandbox/daytona/types.ts +4 -0
- package/src/adapters/sandbox/inmemory/index.ts +22 -16
- package/src/adapters/sandbox/virtual/filesystem.ts +29 -31
- package/src/adapters/sandbox/virtual/index.ts +5 -3
- package/src/adapters/sandbox/virtual/provider.ts +5 -2
- package/src/adapters/sandbox/virtual/types.ts +3 -0
- package/src/adapters/sandbox/virtual/with-virtual-sandbox.ts +4 -3
- package/src/lib/sandbox/tree.integration.test.ts +153 -0
- package/src/lib/sandbox/types.ts +2 -0
- package/src/lib/session/session-edge-cases.integration.test.ts +962 -0
- package/src/lib/session/session.integration.test.ts +852 -0
- package/src/lib/session/session.ts +5 -4
- package/src/lib/skills/skills.integration.test.ts +308 -0
- package/src/lib/state/manager.integration.test.ts +342 -0
- package/src/lib/subagent/subagent.integration.test.ts +467 -0
- package/src/lib/thread/id.test.ts +50 -0
- package/src/lib/tool-router/auto-append-sandbox.integration.test.ts +344 -0
- package/src/lib/tool-router/router-edge-cases.integration.test.ts +623 -0
- package/src/lib/tool-router/router.integration.test.ts +699 -0
- package/src/lib/types.test.ts +29 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { withAutoAppend } from "./auto-append";
|
|
3
|
+
import { withSandbox } from "./with-sandbox";
|
|
4
|
+
import type { RouterContext, ToolHandlerResponse } from "./types";
|
|
5
|
+
import type { ToolResultConfig } from "../types";
|
|
6
|
+
import type { Sandbox } from "../sandbox/types";
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// withAutoAppend
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
describe("withAutoAppend", () => {
|
|
13
|
+
it("appends tool result via threadHandler and sets resultAppended", async () => {
|
|
14
|
+
const appended: ToolResultConfig[] = [];
|
|
15
|
+
const threadHandler = async (config: ToolResultConfig) => {
|
|
16
|
+
appended.push(config);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const innerHandler = async (
|
|
20
|
+
args: { text: string },
|
|
21
|
+
_ctx: RouterContext,
|
|
22
|
+
): Promise<ToolHandlerResponse<{ echoed: string }>> => ({
|
|
23
|
+
toolResponse: `Echo: ${args.text}`,
|
|
24
|
+
data: { echoed: args.text },
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const wrapped = withAutoAppend(threadHandler, innerHandler);
|
|
28
|
+
|
|
29
|
+
const result = await wrapped(
|
|
30
|
+
{ text: "hello" },
|
|
31
|
+
{
|
|
32
|
+
threadId: "thread-1",
|
|
33
|
+
toolCallId: "tc-1",
|
|
34
|
+
toolName: "Echo",
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
expect(result.resultAppended).toBe(true);
|
|
39
|
+
expect(result.toolResponse).toBe("Response appended via withAutoAppend");
|
|
40
|
+
expect(result.data).toEqual({ echoed: "hello" });
|
|
41
|
+
|
|
42
|
+
expect(appended).toHaveLength(1);
|
|
43
|
+
const firstAppended = appended.at(0);
|
|
44
|
+
if (!firstAppended) throw new Error("expected appended item");
|
|
45
|
+
expect(firstAppended.threadId).toBe("thread-1");
|
|
46
|
+
expect(firstAppended.toolCallId).toBe("tc-1");
|
|
47
|
+
expect(firstAppended.toolName).toBe("Echo");
|
|
48
|
+
expect(firstAppended.content).toBe("Echo: hello");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("preserves original data but replaces toolResponse", async () => {
|
|
52
|
+
const threadHandler = async () => {};
|
|
53
|
+
|
|
54
|
+
const innerHandler = async (): Promise<
|
|
55
|
+
ToolHandlerResponse<{ large: string }>
|
|
56
|
+
> => ({
|
|
57
|
+
toolResponse: "A".repeat(10000),
|
|
58
|
+
data: { large: "summary" },
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const wrapped = withAutoAppend(threadHandler, innerHandler);
|
|
62
|
+
|
|
63
|
+
const result = await wrapped(
|
|
64
|
+
{},
|
|
65
|
+
{ threadId: "t", toolCallId: "tc", toolName: "BigTool" },
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
expect(result.toolResponse).toBe("Response appended via withAutoAppend");
|
|
69
|
+
expect(result.data).toEqual({ large: "summary" });
|
|
70
|
+
expect(result.resultAppended).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("propagates handler errors without appending", async () => {
|
|
74
|
+
const appendSpy = vi.fn();
|
|
75
|
+
const threadHandler = appendSpy;
|
|
76
|
+
|
|
77
|
+
const innerHandler = async (): Promise<ToolHandlerResponse<null>> => {
|
|
78
|
+
throw new Error("handler failed");
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const wrapped = withAutoAppend(threadHandler, innerHandler);
|
|
82
|
+
|
|
83
|
+
await expect(
|
|
84
|
+
wrapped(
|
|
85
|
+
{},
|
|
86
|
+
{ threadId: "t", toolCallId: "tc", toolName: "Fail" },
|
|
87
|
+
),
|
|
88
|
+
).rejects.toThrow("handler failed");
|
|
89
|
+
|
|
90
|
+
expect(appendSpy).not.toHaveBeenCalled();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("uses correct context fields for thread handler config", async () => {
|
|
94
|
+
let capturedConfig: ToolResultConfig | null = null;
|
|
95
|
+
const threadHandler = async (config: ToolResultConfig) => {
|
|
96
|
+
capturedConfig = config;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const innerHandler = async (): Promise<ToolHandlerResponse<null>> => ({
|
|
100
|
+
toolResponse: "result content",
|
|
101
|
+
data: null,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const wrapped = withAutoAppend(threadHandler, innerHandler);
|
|
105
|
+
|
|
106
|
+
await wrapped(
|
|
107
|
+
{},
|
|
108
|
+
{
|
|
109
|
+
threadId: "my-thread",
|
|
110
|
+
toolCallId: "my-tc",
|
|
111
|
+
toolName: "MyTool",
|
|
112
|
+
sandboxId: "sb-1",
|
|
113
|
+
},
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
expect(capturedConfig).toEqual({
|
|
117
|
+
threadId: "my-thread",
|
|
118
|
+
toolCallId: "my-tc",
|
|
119
|
+
toolName: "MyTool",
|
|
120
|
+
content: "result content",
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
// withSandbox
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
|
|
129
|
+
describe("withSandbox", () => {
|
|
130
|
+
function createMockSandbox(): Sandbox {
|
|
131
|
+
return {
|
|
132
|
+
id: "mock-sandbox",
|
|
133
|
+
capabilities: {
|
|
134
|
+
filesystem: true,
|
|
135
|
+
execution: true,
|
|
136
|
+
persistence: false,
|
|
137
|
+
},
|
|
138
|
+
fs: {
|
|
139
|
+
workspaceBase: "/",
|
|
140
|
+
exists: async () => false,
|
|
141
|
+
stat: async () => ({
|
|
142
|
+
isFile: false,
|
|
143
|
+
isDirectory: false,
|
|
144
|
+
isSymbolicLink: false,
|
|
145
|
+
size: 0,
|
|
146
|
+
mtime: new Date(),
|
|
147
|
+
}),
|
|
148
|
+
readdir: async () => [],
|
|
149
|
+
readdirWithFileTypes: async () => [],
|
|
150
|
+
readFile: async () => "",
|
|
151
|
+
readFileBuffer: async () => new Uint8Array(),
|
|
152
|
+
writeFile: async () => {},
|
|
153
|
+
appendFile: async () => {},
|
|
154
|
+
mkdir: async () => {},
|
|
155
|
+
rm: async () => {},
|
|
156
|
+
cp: async () => {},
|
|
157
|
+
mv: async () => {},
|
|
158
|
+
readlink: async () => "",
|
|
159
|
+
resolvePath: (base: string, path: string) => base + "/" + path,
|
|
160
|
+
},
|
|
161
|
+
exec: async () => ({ stdout: "", stderr: "", exitCode: 0 }),
|
|
162
|
+
destroy: async () => {},
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
it("resolves sandbox and passes it to handler", async () => {
|
|
167
|
+
const mockSandbox = createMockSandbox();
|
|
168
|
+
const manager = {
|
|
169
|
+
getSandbox: async (id: string) => {
|
|
170
|
+
expect(id).toBe("sb-42");
|
|
171
|
+
return mockSandbox;
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
let capturedSandbox: Sandbox | null = null;
|
|
176
|
+
let capturedSandboxId: string | null = null;
|
|
177
|
+
|
|
178
|
+
const handler = async (
|
|
179
|
+
_args: { text: string },
|
|
180
|
+
ctx: RouterContext & { sandbox: Sandbox; sandboxId: string },
|
|
181
|
+
): Promise<ToolHandlerResponse<null>> => {
|
|
182
|
+
capturedSandbox = ctx.sandbox;
|
|
183
|
+
capturedSandboxId = ctx.sandboxId;
|
|
184
|
+
return { toolResponse: "ok", data: null };
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const wrapped = withSandbox(manager, handler);
|
|
188
|
+
|
|
189
|
+
const result = await wrapped(
|
|
190
|
+
{ text: "hello" },
|
|
191
|
+
{
|
|
192
|
+
threadId: "thread-1",
|
|
193
|
+
toolCallId: "tc-1",
|
|
194
|
+
toolName: "Test",
|
|
195
|
+
sandboxId: "sb-42",
|
|
196
|
+
},
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
expect(result.toolResponse).toBe("ok");
|
|
200
|
+
expect(capturedSandbox).toBe(mockSandbox);
|
|
201
|
+
expect(capturedSandboxId).toBe("sb-42");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("returns error when no sandboxId is present", async () => {
|
|
205
|
+
const manager = {
|
|
206
|
+
getSandbox: vi.fn(),
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const handler = async (): Promise<ToolHandlerResponse<string>> => ({
|
|
210
|
+
toolResponse: "should not run",
|
|
211
|
+
data: "nope",
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const wrapped = withSandbox(manager, handler);
|
|
215
|
+
|
|
216
|
+
const result = await wrapped(
|
|
217
|
+
{},
|
|
218
|
+
{
|
|
219
|
+
threadId: "thread-1",
|
|
220
|
+
toolCallId: "tc-1",
|
|
221
|
+
toolName: "Bash",
|
|
222
|
+
},
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
expect(result.toolResponse).toContain("No sandbox configured");
|
|
226
|
+
expect(result.toolResponse).toContain("Bash");
|
|
227
|
+
expect(result.data).toBeNull();
|
|
228
|
+
expect(manager.getSandbox).not.toHaveBeenCalled();
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("returns error when sandboxId is undefined explicitly", async () => {
|
|
232
|
+
const manager = {
|
|
233
|
+
getSandbox: vi.fn(),
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const handler = async (): Promise<ToolHandlerResponse<null>> => ({
|
|
237
|
+
toolResponse: "nope",
|
|
238
|
+
data: null,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
const wrapped = withSandbox(manager, handler);
|
|
242
|
+
|
|
243
|
+
const result = await wrapped(
|
|
244
|
+
{},
|
|
245
|
+
{
|
|
246
|
+
threadId: "t",
|
|
247
|
+
toolCallId: "tc",
|
|
248
|
+
toolName: "Grep",
|
|
249
|
+
sandboxId: undefined,
|
|
250
|
+
},
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
expect(result.toolResponse).toContain("No sandbox configured");
|
|
254
|
+
expect(result.data).toBeNull();
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it("propagates getSandbox errors", async () => {
|
|
258
|
+
const manager = {
|
|
259
|
+
getSandbox: async () => {
|
|
260
|
+
throw new Error("sandbox not found");
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const handler = async (): Promise<ToolHandlerResponse<null>> => ({
|
|
265
|
+
toolResponse: "ok",
|
|
266
|
+
data: null,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
const wrapped = withSandbox(manager, handler);
|
|
270
|
+
|
|
271
|
+
await expect(
|
|
272
|
+
wrapped(
|
|
273
|
+
{},
|
|
274
|
+
{
|
|
275
|
+
threadId: "t",
|
|
276
|
+
toolCallId: "tc",
|
|
277
|
+
toolName: "Test",
|
|
278
|
+
sandboxId: "sb-missing",
|
|
279
|
+
},
|
|
280
|
+
),
|
|
281
|
+
).rejects.toThrow("sandbox not found");
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it("passes all RouterContext fields through to inner handler", async () => {
|
|
285
|
+
const mockSandbox = createMockSandbox();
|
|
286
|
+
const manager = { getSandbox: async () => mockSandbox };
|
|
287
|
+
|
|
288
|
+
let capturedCtx: (RouterContext & { sandbox: Sandbox; sandboxId: string }) | null = null;
|
|
289
|
+
|
|
290
|
+
const handler = async (
|
|
291
|
+
_args: unknown,
|
|
292
|
+
ctx: RouterContext & { sandbox: Sandbox; sandboxId: string },
|
|
293
|
+
): Promise<ToolHandlerResponse<null>> => {
|
|
294
|
+
capturedCtx = ctx;
|
|
295
|
+
return { toolResponse: "ok", data: null };
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
const wrapped = withSandbox(manager, handler);
|
|
299
|
+
|
|
300
|
+
await wrapped(
|
|
301
|
+
{},
|
|
302
|
+
{
|
|
303
|
+
threadId: "my-thread",
|
|
304
|
+
toolCallId: "my-tc",
|
|
305
|
+
toolName: "MyTool",
|
|
306
|
+
sandboxId: "my-sandbox",
|
|
307
|
+
},
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
expect(capturedCtx).toEqual(
|
|
311
|
+
expect.objectContaining({
|
|
312
|
+
threadId: "my-thread",
|
|
313
|
+
toolCallId: "my-tc",
|
|
314
|
+
toolName: "MyTool",
|
|
315
|
+
sandboxId: "my-sandbox",
|
|
316
|
+
sandbox: mockSandbox,
|
|
317
|
+
}),
|
|
318
|
+
);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it("handles sandboxId with empty string as falsy", async () => {
|
|
322
|
+
const manager = { getSandbox: vi.fn() };
|
|
323
|
+
|
|
324
|
+
const handler = async (): Promise<ToolHandlerResponse<null>> => ({
|
|
325
|
+
toolResponse: "nope",
|
|
326
|
+
data: null,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
const wrapped = withSandbox(manager, handler);
|
|
330
|
+
|
|
331
|
+
const result = await wrapped(
|
|
332
|
+
{},
|
|
333
|
+
{
|
|
334
|
+
threadId: "t",
|
|
335
|
+
toolCallId: "tc",
|
|
336
|
+
toolName: "Test",
|
|
337
|
+
sandboxId: "",
|
|
338
|
+
},
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
expect(result.toolResponse).toContain("No sandbox configured");
|
|
342
|
+
expect(manager.getSandbox).not.toHaveBeenCalled();
|
|
343
|
+
});
|
|
344
|
+
});
|