zeitlich 0.2.15 → 0.2.17
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 +125 -64
- 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 +83 -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 +80 -38
- 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.cjs +2 -2
- package/dist/adapters/thread/langchain/index.cjs.map +1 -1
- package/dist/adapters/thread/langchain/index.d.cts +2 -2
- package/dist/adapters/thread/langchain/index.d.ts +2 -2
- package/dist/adapters/thread/langchain/index.js +2 -2
- package/dist/adapters/thread/langchain/index.js.map +1 -1
- package/dist/index.cjs +102 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +98 -11
- package/dist/index.js.map +1 -1
- package/dist/{types-CwwgQ_9H.d.ts → queries-BlC1I3DK.d.ts} +48 -3
- package/dist/{types-BVP87m_W.d.cts → queries-DlJ3jE48.d.cts} +48 -3
- 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-Dje1TdH6.d.cts → types-Bh-BbfCp.d.cts} +31 -12
- package/dist/{types-BWvIYK28.d.ts → types-NkiAxU4t.d.ts} +31 -12
- package/dist/workflow.cjs +102 -10
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +114 -40
- package/dist/workflow.d.ts +114 -40
- package/dist/workflow.js +98 -11
- 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 +7 -3
- package/src/adapters/sandbox/virtual/provider.ts +5 -2
- package/src/adapters/sandbox/virtual/queries.ts +97 -0
- package/src/adapters/sandbox/virtual/types.ts +3 -0
- package/src/adapters/sandbox/virtual/with-virtual-sandbox.ts +4 -3
- package/src/adapters/thread/langchain/activities.ts +7 -5
- 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 +853 -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/define.ts +34 -47
- package/src/lib/subagent/handler.ts +9 -6
- package/src/lib/subagent/index.ts +4 -1
- package/src/lib/subagent/subagent.integration.test.ts +573 -0
- package/src/lib/subagent/types.ts +40 -10
- package/src/lib/subagent/workflow.ts +114 -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
- package/src/lib/workflow.test.ts +131 -0
- package/src/lib/workflow.ts +45 -0
- package/src/workflow.ts +12 -2
|
@@ -15,7 +15,7 @@ import { createVirtualSandbox } from "./index";
|
|
|
15
15
|
* the parent workflow for the current file tree and resolver context.
|
|
16
16
|
*
|
|
17
17
|
* On each invocation the wrapper:
|
|
18
|
-
* 1. Queries the workflow's `AgentState` for `fileTree` and `
|
|
18
|
+
* 1. Queries the workflow's `AgentState` for `fileTree`, `resolverContext`, and `workspaceBase`
|
|
19
19
|
* 2. Creates an ephemeral {@link VirtualSandbox} from tree + provider's resolver
|
|
20
20
|
* 3. Runs the inner handler
|
|
21
21
|
* 4. Returns the handler's result together with any {@link TreeMutation}s
|
|
@@ -63,7 +63,7 @@ export function withVirtualSandbox<
|
|
|
63
63
|
const state =
|
|
64
64
|
await queryParentWorkflowState<VirtualSandboxState<TCtx, TMeta>>(client);
|
|
65
65
|
|
|
66
|
-
const { sandboxId, fileTree, resolverContext } = state;
|
|
66
|
+
const { sandboxId, fileTree, resolverContext, workspaceBase } = state;
|
|
67
67
|
if (!fileTree || !sandboxId) {
|
|
68
68
|
return {
|
|
69
69
|
toolResponse: `Error: No fileTree/sandboxId in agent state. The ${context.toolName} tool requires a virtual sandbox.`,
|
|
@@ -75,7 +75,8 @@ export function withVirtualSandbox<
|
|
|
75
75
|
sandboxId,
|
|
76
76
|
fileTree,
|
|
77
77
|
provider.resolver,
|
|
78
|
-
resolverContext
|
|
78
|
+
resolverContext,
|
|
79
|
+
workspaceBase ?? "/",
|
|
79
80
|
);
|
|
80
81
|
const response = await handler(args, { ...context, sandbox });
|
|
81
82
|
const mutations = sandbox.fs.getMutations();
|
|
@@ -96,7 +96,7 @@ export function createLangChainAdapter(
|
|
|
96
96
|
|
|
97
97
|
async forkThread(
|
|
98
98
|
sourceThreadId: string,
|
|
99
|
-
targetThreadId: string
|
|
99
|
+
targetThreadId: string
|
|
100
100
|
): Promise<void> {
|
|
101
101
|
const thread = createLangChainThreadManager({
|
|
102
102
|
redis,
|
|
@@ -106,18 +106,20 @@ export function createLangChainAdapter(
|
|
|
106
106
|
},
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
const makeInvoker = (
|
|
110
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
111
|
+
model: BaseChatModel<any>
|
|
112
|
+
): ModelInvoker<StoredMessage> =>
|
|
111
113
|
createLangChainModelInvoker({ redis, model });
|
|
112
114
|
|
|
113
115
|
const invoker: ModelInvoker<StoredMessage> = config.model
|
|
114
116
|
? makeInvoker(config.model)
|
|
115
|
-
: (
|
|
117
|
+
: () => {
|
|
116
118
|
throw new Error(
|
|
117
119
|
"No default model provided to createLangChainAdapter. " +
|
|
118
120
|
"Either pass `model` in the config or use `createModelInvoker(model)` instead."
|
|
119
121
|
);
|
|
120
|
-
}
|
|
122
|
+
};
|
|
121
123
|
|
|
122
124
|
return {
|
|
123
125
|
threadOps,
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { toTree } from "./tree";
|
|
3
|
+
import type { SandboxFileSystem, DirentEntry } from "./types";
|
|
4
|
+
|
|
5
|
+
function createMockFs(
|
|
6
|
+
structure: Record<string, { isDir: boolean; isLink?: boolean; linkTarget?: string }>,
|
|
7
|
+
): SandboxFileSystem {
|
|
8
|
+
return {
|
|
9
|
+
workspaceBase: "/",
|
|
10
|
+
exists: async (path: string) => path in structure,
|
|
11
|
+
stat: async (path: string) => {
|
|
12
|
+
const entry = structure[path];
|
|
13
|
+
return {
|
|
14
|
+
isFile: entry ? !entry.isDir : false,
|
|
15
|
+
isDirectory: entry ? entry.isDir : false,
|
|
16
|
+
isSymbolicLink: entry?.isLink ?? false,
|
|
17
|
+
size: 0,
|
|
18
|
+
mtime: new Date(),
|
|
19
|
+
};
|
|
20
|
+
},
|
|
21
|
+
readdir: async (dir: string) => {
|
|
22
|
+
const prefix = dir.endsWith("/") ? dir : dir + "/";
|
|
23
|
+
return Object.keys(structure)
|
|
24
|
+
.filter((p) => p.startsWith(prefix) && !p.slice(prefix.length).includes("/"))
|
|
25
|
+
.map((p) => p.slice(prefix.length));
|
|
26
|
+
},
|
|
27
|
+
readdirWithFileTypes: async (dir: string): Promise<DirentEntry[]> => {
|
|
28
|
+
const prefix = dir.endsWith("/") ? dir : dir + "/";
|
|
29
|
+
return Object.entries(structure)
|
|
30
|
+
.filter(([p]) => p.startsWith(prefix) && !p.slice(prefix.length).includes("/"))
|
|
31
|
+
.map(([p, meta]) => ({
|
|
32
|
+
name: p.slice(prefix.length),
|
|
33
|
+
isFile: !meta.isDir && !meta.isLink,
|
|
34
|
+
isDirectory: meta.isDir,
|
|
35
|
+
isSymbolicLink: meta.isLink ?? false,
|
|
36
|
+
}));
|
|
37
|
+
},
|
|
38
|
+
readFile: async () => "",
|
|
39
|
+
readFileBuffer: async () => new Uint8Array(),
|
|
40
|
+
writeFile: async () => {},
|
|
41
|
+
appendFile: async () => {},
|
|
42
|
+
mkdir: async () => {},
|
|
43
|
+
rm: async () => {},
|
|
44
|
+
cp: async () => {},
|
|
45
|
+
mv: async () => {},
|
|
46
|
+
readlink: async (path: string) => structure[path]?.linkTarget ?? "",
|
|
47
|
+
resolvePath: (base: string, path: string) => base + "/" + path,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
describe("toTree", () => {
|
|
52
|
+
it("renders a flat directory", async () => {
|
|
53
|
+
const fs = createMockFs({
|
|
54
|
+
"/a.txt": { isDir: false },
|
|
55
|
+
"/b.txt": { isDir: false },
|
|
56
|
+
"/c.txt": { isDir: false },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const tree = await toTree(fs);
|
|
60
|
+
expect(tree).toContain("a.txt");
|
|
61
|
+
expect(tree).toContain("b.txt");
|
|
62
|
+
expect(tree).toContain("c.txt");
|
|
63
|
+
expect(tree).toContain("├─");
|
|
64
|
+
expect(tree).toContain("└─");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("renders nested directories", async () => {
|
|
68
|
+
const fs = createMockFs({
|
|
69
|
+
"/src": { isDir: true },
|
|
70
|
+
"/src/index.ts": { isDir: false },
|
|
71
|
+
"/src/utils.ts": { isDir: false },
|
|
72
|
+
"/package.json": { isDir: false },
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const tree = await toTree(fs);
|
|
76
|
+
expect(tree).toContain("src/");
|
|
77
|
+
expect(tree).toContain("index.ts");
|
|
78
|
+
expect(tree).toContain("utils.ts");
|
|
79
|
+
expect(tree).toContain("package.json");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("sorts directories before files", async () => {
|
|
83
|
+
const fs = createMockFs({
|
|
84
|
+
"/zebra.txt": { isDir: false },
|
|
85
|
+
"/alpha": { isDir: true },
|
|
86
|
+
"/beta.txt": { isDir: false },
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const tree = await toTree(fs);
|
|
90
|
+
const lines = tree.split("\n");
|
|
91
|
+
const alphaLine = lines.findIndex((l) => l.includes("alpha/"));
|
|
92
|
+
const zebraLine = lines.findIndex((l) => l.includes("zebra.txt"));
|
|
93
|
+
expect(alphaLine).toBeLessThan(zebraLine);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("renders symlinks with target", async () => {
|
|
97
|
+
const fs = createMockFs({
|
|
98
|
+
"/link.txt": { isDir: false, isLink: true, linkTarget: "/real.txt" },
|
|
99
|
+
"/real.txt": { isDir: false },
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const tree = await toTree(fs);
|
|
103
|
+
expect(tree).toContain("link.txt");
|
|
104
|
+
expect(tree).toContain("→");
|
|
105
|
+
expect(tree).toContain("/real.txt");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("respects depth limit", async () => {
|
|
109
|
+
const fs = createMockFs({
|
|
110
|
+
"/a": { isDir: true },
|
|
111
|
+
"/a/b": { isDir: true },
|
|
112
|
+
"/a/b/c.txt": { isDir: false },
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const shallowTree = await toTree(fs, { depth: 1 });
|
|
116
|
+
expect(shallowTree).toContain("a/");
|
|
117
|
+
expect(shallowTree).toContain("(...)");
|
|
118
|
+
expect(shallowTree).not.toContain("c.txt");
|
|
119
|
+
|
|
120
|
+
const deepTree = await toTree(fs, { depth: 3 });
|
|
121
|
+
expect(deepTree).toContain("a/");
|
|
122
|
+
expect(deepTree).toContain("b/");
|
|
123
|
+
expect(deepTree).toContain("c.txt");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("renders empty directory", async () => {
|
|
127
|
+
const fs = createMockFs({});
|
|
128
|
+
|
|
129
|
+
const tree = await toTree(fs);
|
|
130
|
+
expect(tree).toContain("/");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("disables sorting when sort=false", async () => {
|
|
134
|
+
const fs = createMockFs({
|
|
135
|
+
"/z.txt": { isDir: false },
|
|
136
|
+
"/a.txt": { isDir: false },
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const tree = await toTree(fs, { sort: false });
|
|
140
|
+
expect(tree).toContain("z.txt");
|
|
141
|
+
expect(tree).toContain("a.txt");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("uses custom start directory", async () => {
|
|
145
|
+
const fs = createMockFs({
|
|
146
|
+
"/home/user/file.txt": { isDir: false },
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const tree = await toTree(fs, { dir: "/home/user" });
|
|
150
|
+
expect(tree).toContain("user/");
|
|
151
|
+
expect(tree).toContain("file.txt");
|
|
152
|
+
});
|
|
153
|
+
});
|
package/src/lib/sandbox/types.ts
CHANGED
|
@@ -24,6 +24,8 @@ export interface FileStat {
|
|
|
24
24
|
* {@link SandboxNotSupportedError}.
|
|
25
25
|
*/
|
|
26
26
|
export interface SandboxFileSystem {
|
|
27
|
+
/** Base directory used when resolving relative paths. */
|
|
28
|
+
readonly workspaceBase: string;
|
|
27
29
|
readFile(path: string): Promise<string>;
|
|
28
30
|
readFileBuffer(path: string): Promise<Uint8Array>;
|
|
29
31
|
writeFile(path: string, content: string | Uint8Array): Promise<void>;
|