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.
Files changed (78) hide show
  1. package/README.md +125 -64
  2. package/dist/adapters/sandbox/daytona/index.cjs +52 -23
  3. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  4. package/dist/adapters/sandbox/daytona/index.d.cts +10 -2
  5. package/dist/adapters/sandbox/daytona/index.d.ts +10 -2
  6. package/dist/adapters/sandbox/daytona/index.js +52 -23
  7. package/dist/adapters/sandbox/daytona/index.js.map +1 -1
  8. package/dist/adapters/sandbox/inmemory/index.cjs +21 -16
  9. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
  10. package/dist/adapters/sandbox/inmemory/index.d.cts +1 -1
  11. package/dist/adapters/sandbox/inmemory/index.d.ts +1 -1
  12. package/dist/adapters/sandbox/inmemory/index.js +21 -16
  13. package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
  14. package/dist/adapters/sandbox/virtual/index.cjs +83 -38
  15. package/dist/adapters/sandbox/virtual/index.cjs.map +1 -1
  16. package/dist/adapters/sandbox/virtual/index.d.cts +6 -6
  17. package/dist/adapters/sandbox/virtual/index.d.ts +6 -6
  18. package/dist/adapters/sandbox/virtual/index.js +80 -38
  19. package/dist/adapters/sandbox/virtual/index.js.map +1 -1
  20. package/dist/adapters/thread/google-genai/index.d.cts +2 -2
  21. package/dist/adapters/thread/google-genai/index.d.ts +2 -2
  22. package/dist/adapters/thread/langchain/index.cjs +2 -2
  23. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  24. package/dist/adapters/thread/langchain/index.d.cts +2 -2
  25. package/dist/adapters/thread/langchain/index.d.ts +2 -2
  26. package/dist/adapters/thread/langchain/index.js +2 -2
  27. package/dist/adapters/thread/langchain/index.js.map +1 -1
  28. package/dist/index.cjs +102 -10
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.cts +6 -6
  31. package/dist/index.d.ts +6 -6
  32. package/dist/index.js +98 -11
  33. package/dist/index.js.map +1 -1
  34. package/dist/{types-CwwgQ_9H.d.ts → queries-BlC1I3DK.d.ts} +48 -3
  35. package/dist/{types-BVP87m_W.d.cts → queries-DlJ3jE48.d.cts} +48 -3
  36. package/dist/{types-CDubRtad.d.cts → types-BMRzfELQ.d.cts} +2 -0
  37. package/dist/{types-CDubRtad.d.ts → types-BMRzfELQ.d.ts} +2 -0
  38. package/dist/{types-Dje1TdH6.d.cts → types-Bh-BbfCp.d.cts} +31 -12
  39. package/dist/{types-BWvIYK28.d.ts → types-NkiAxU4t.d.ts} +31 -12
  40. package/dist/workflow.cjs +102 -10
  41. package/dist/workflow.cjs.map +1 -1
  42. package/dist/workflow.d.cts +114 -40
  43. package/dist/workflow.d.ts +114 -40
  44. package/dist/workflow.js +98 -11
  45. package/dist/workflow.js.map +1 -1
  46. package/package.json +1 -1
  47. package/src/adapters/sandbox/daytona/filesystem.ts +43 -19
  48. package/src/adapters/sandbox/daytona/index.ts +16 -3
  49. package/src/adapters/sandbox/daytona/types.ts +4 -0
  50. package/src/adapters/sandbox/inmemory/index.ts +22 -16
  51. package/src/adapters/sandbox/virtual/filesystem.ts +29 -31
  52. package/src/adapters/sandbox/virtual/index.ts +7 -3
  53. package/src/adapters/sandbox/virtual/provider.ts +5 -2
  54. package/src/adapters/sandbox/virtual/queries.ts +97 -0
  55. package/src/adapters/sandbox/virtual/types.ts +3 -0
  56. package/src/adapters/sandbox/virtual/with-virtual-sandbox.ts +4 -3
  57. package/src/adapters/thread/langchain/activities.ts +7 -5
  58. package/src/lib/sandbox/tree.integration.test.ts +153 -0
  59. package/src/lib/sandbox/types.ts +2 -0
  60. package/src/lib/session/session-edge-cases.integration.test.ts +962 -0
  61. package/src/lib/session/session.integration.test.ts +853 -0
  62. package/src/lib/session/session.ts +5 -4
  63. package/src/lib/skills/skills.integration.test.ts +308 -0
  64. package/src/lib/state/manager.integration.test.ts +342 -0
  65. package/src/lib/subagent/define.ts +34 -47
  66. package/src/lib/subagent/handler.ts +9 -6
  67. package/src/lib/subagent/index.ts +4 -1
  68. package/src/lib/subagent/subagent.integration.test.ts +573 -0
  69. package/src/lib/subagent/types.ts +40 -10
  70. package/src/lib/subagent/workflow.ts +114 -0
  71. package/src/lib/thread/id.test.ts +50 -0
  72. package/src/lib/tool-router/auto-append-sandbox.integration.test.ts +344 -0
  73. package/src/lib/tool-router/router-edge-cases.integration.test.ts +623 -0
  74. package/src/lib/tool-router/router.integration.test.ts +699 -0
  75. package/src/lib/types.test.ts +29 -0
  76. package/src/lib/workflow.test.ts +131 -0
  77. package/src/lib/workflow.ts +45 -0
  78. 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 `resolverContext`
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
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
- const makeInvoker = (model: BaseChatModel<any>): ModelInvoker<StoredMessage> =>
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
- }) as unknown as ModelInvoker<StoredMessage>);
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
+ });
@@ -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>;