zeitlich 0.2.13 → 0.2.15

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 (135) hide show
  1. package/README.md +61 -50
  2. package/dist/adapters/sandbox/daytona/index.cjs +205 -0
  3. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -0
  4. package/dist/adapters/sandbox/daytona/index.d.cts +86 -0
  5. package/dist/adapters/sandbox/daytona/index.d.ts +86 -0
  6. package/dist/adapters/sandbox/daytona/index.js +202 -0
  7. package/dist/adapters/sandbox/daytona/index.js.map +1 -0
  8. package/dist/adapters/sandbox/inmemory/index.cjs +174 -0
  9. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -0
  10. package/dist/adapters/sandbox/inmemory/index.d.cts +28 -0
  11. package/dist/adapters/sandbox/inmemory/index.d.ts +28 -0
  12. package/dist/adapters/sandbox/inmemory/index.js +172 -0
  13. package/dist/adapters/sandbox/inmemory/index.js.map +1 -0
  14. package/dist/adapters/sandbox/virtual/index.cjs +405 -0
  15. package/dist/adapters/sandbox/virtual/index.cjs.map +1 -0
  16. package/dist/adapters/sandbox/virtual/index.d.cts +85 -0
  17. package/dist/adapters/sandbox/virtual/index.d.ts +85 -0
  18. package/dist/adapters/sandbox/virtual/index.js +400 -0
  19. package/dist/adapters/sandbox/virtual/index.js.map +1 -0
  20. package/dist/adapters/thread/google-genai/index.cjs +306 -0
  21. package/dist/adapters/thread/google-genai/index.cjs.map +1 -0
  22. package/dist/adapters/thread/google-genai/index.d.cts +145 -0
  23. package/dist/adapters/thread/google-genai/index.d.ts +145 -0
  24. package/dist/adapters/thread/google-genai/index.js +300 -0
  25. package/dist/adapters/thread/google-genai/index.js.map +1 -0
  26. package/dist/adapters/{langchain → thread/langchain}/index.cjs +29 -9
  27. package/dist/adapters/thread/langchain/index.cjs.map +1 -0
  28. package/dist/adapters/{langchain → thread/langchain}/index.d.cts +17 -21
  29. package/dist/adapters/{langchain → thread/langchain}/index.d.ts +17 -21
  30. package/dist/adapters/{langchain → thread/langchain}/index.js +29 -9
  31. package/dist/adapters/thread/langchain/index.js.map +1 -0
  32. package/dist/index.cjs +866 -567
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +235 -74
  35. package/dist/index.d.ts +235 -74
  36. package/dist/index.js +854 -562
  37. package/dist/index.js.map +1 -1
  38. package/dist/{thread-manager-qc0g5Rvd.d.cts → types-35POpVfa.d.cts} +7 -6
  39. package/dist/{thread-manager-qc0g5Rvd.d.ts → types-35POpVfa.d.ts} +7 -6
  40. package/dist/types-BMXzv7TN.d.cts +476 -0
  41. package/dist/types-BMXzv7TN.d.ts +476 -0
  42. package/dist/types-BVP87m_W.d.cts +121 -0
  43. package/dist/types-BWvIYK28.d.ts +391 -0
  44. package/dist/types-CDubRtad.d.cts +115 -0
  45. package/dist/types-CDubRtad.d.ts +115 -0
  46. package/dist/types-CwwgQ_9H.d.ts +121 -0
  47. package/dist/types-Dje1TdH6.d.cts +391 -0
  48. package/dist/workflow.cjs +460 -321
  49. package/dist/workflow.cjs.map +1 -1
  50. package/dist/workflow.d.cts +271 -222
  51. package/dist/workflow.d.ts +271 -222
  52. package/dist/workflow.js +456 -319
  53. package/dist/workflow.js.map +1 -1
  54. package/package.json +65 -8
  55. package/src/adapters/sandbox/daytona/filesystem.ts +136 -0
  56. package/src/adapters/sandbox/daytona/index.ts +149 -0
  57. package/src/adapters/sandbox/daytona/types.ts +34 -0
  58. package/src/adapters/sandbox/inmemory/index.ts +213 -0
  59. package/src/adapters/sandbox/virtual/filesystem.ts +345 -0
  60. package/src/adapters/sandbox/virtual/index.ts +88 -0
  61. package/src/adapters/sandbox/virtual/mutations.ts +38 -0
  62. package/src/adapters/sandbox/virtual/provider.ts +101 -0
  63. package/src/adapters/sandbox/virtual/tree.ts +82 -0
  64. package/src/adapters/sandbox/virtual/types.ts +127 -0
  65. package/src/adapters/sandbox/virtual/virtual-sandbox.test.ts +523 -0
  66. package/src/adapters/sandbox/virtual/with-virtual-sandbox.ts +91 -0
  67. package/src/adapters/thread/google-genai/activities.ts +132 -0
  68. package/src/adapters/thread/google-genai/index.ts +41 -0
  69. package/src/adapters/thread/google-genai/model-invoker.ts +154 -0
  70. package/src/adapters/thread/google-genai/thread-manager.ts +169 -0
  71. package/src/adapters/{langchain → thread/langchain}/activities.ts +22 -15
  72. package/src/adapters/{langchain → thread/langchain}/index.ts +1 -1
  73. package/src/adapters/{langchain → thread/langchain}/model-invoker.ts +15 -18
  74. package/src/adapters/{langchain → thread/langchain}/thread-manager.ts +1 -1
  75. package/src/index.ts +32 -24
  76. package/src/lib/activity.ts +87 -0
  77. package/src/lib/hooks/index.ts +11 -0
  78. package/src/lib/hooks/types.ts +98 -0
  79. package/src/lib/model/helpers.ts +6 -0
  80. package/src/lib/model/index.ts +13 -0
  81. package/src/lib/{model-invoker.ts → model/types.ts} +18 -1
  82. package/src/lib/sandbox/index.ts +19 -0
  83. package/src/lib/sandbox/manager.ts +76 -0
  84. package/src/lib/sandbox/sandbox.test.ts +158 -0
  85. package/src/lib/{fs.ts → sandbox/tree.ts} +6 -6
  86. package/src/lib/sandbox/types.ts +164 -0
  87. package/src/lib/session/index.ts +11 -0
  88. package/src/lib/{session.ts → session/session.ts} +83 -50
  89. package/src/lib/session/types.ts +95 -0
  90. package/src/lib/skills/fs-provider.ts +16 -15
  91. package/src/lib/skills/handler.ts +31 -0
  92. package/src/lib/skills/index.ts +5 -1
  93. package/src/lib/skills/register.ts +20 -0
  94. package/src/lib/skills/tool.ts +47 -0
  95. package/src/lib/state/index.ts +9 -0
  96. package/src/lib/{state-manager.ts → state/manager.ts} +10 -147
  97. package/src/lib/state/types.ts +134 -0
  98. package/src/lib/subagent/define.ts +71 -0
  99. package/src/lib/subagent/handler.ts +99 -0
  100. package/src/lib/subagent/index.ts +13 -0
  101. package/src/lib/subagent/register.ts +68 -0
  102. package/src/lib/subagent/tool.ts +80 -0
  103. package/src/lib/subagent/types.ts +92 -0
  104. package/src/lib/thread/index.ts +7 -0
  105. package/src/lib/{thread-manager.ts → thread/manager.ts} +20 -33
  106. package/src/lib/thread/types.ts +39 -0
  107. package/src/lib/tool-router/auto-append.ts +55 -0
  108. package/src/lib/tool-router/index.ts +41 -0
  109. package/src/lib/tool-router/router.ts +462 -0
  110. package/src/lib/tool-router/types.ts +478 -0
  111. package/src/lib/tool-router/with-sandbox.ts +70 -0
  112. package/src/lib/types.ts +5 -382
  113. package/src/tools/bash/bash.test.ts +53 -55
  114. package/src/tools/bash/handler.ts +23 -51
  115. package/src/tools/edit/handler.ts +67 -81
  116. package/src/tools/glob/handler.ts +60 -17
  117. package/src/tools/read-file/handler.ts +67 -0
  118. package/src/tools/read-skill/handler.ts +1 -31
  119. package/src/tools/read-skill/tool.ts +5 -47
  120. package/src/tools/subagent/handler.ts +1 -100
  121. package/src/tools/subagent/tool.ts +5 -93
  122. package/src/tools/task-create/handler.ts +1 -1
  123. package/src/tools/task-get/handler.ts +1 -1
  124. package/src/tools/task-list/handler.ts +1 -1
  125. package/src/tools/task-update/handler.ts +1 -1
  126. package/src/tools/write-file/handler.ts +47 -0
  127. package/src/workflow.ts +88 -47
  128. package/tsup.config.ts +8 -1
  129. package/dist/adapters/langchain/index.cjs.map +0 -1
  130. package/dist/adapters/langchain/index.js.map +0 -1
  131. package/dist/model-invoker-y_zlyMqu.d.cts +0 -892
  132. package/dist/model-invoker-y_zlyMqu.d.ts +0 -892
  133. package/src/lib/tool-router.ts +0 -977
  134. package/src/lib/workflow-helpers.ts +0 -50
  135. /package/src/lib/{thread-id.ts → thread/id.ts} +0 -0
@@ -0,0 +1,80 @@
1
+ import z from "zod";
2
+ import type { SubagentConfig } from "./types";
3
+
4
+ export const SUBAGENT_TOOL_NAME = "Subagent" as const;
5
+
6
+ function buildSubagentDescription(subagents: SubagentConfig[]): string {
7
+ const subagentList = subagents
8
+ .map((s) => {
9
+ const continuation = s.allowThreadContinuation
10
+ ? "\n*(Supports thread continuation — pass a threadId to resume a previous conversation)*"
11
+ : "";
12
+ return `## ${s.agentName}\n${s.description}${continuation}`;
13
+ })
14
+ .join("\n\n");
15
+
16
+ return `The ${SUBAGENT_TOOL_NAME} tool launches specialized agents (subagents) that autonomously handle complex work. Each agent type has specific capabilities and tools available to it.
17
+
18
+ # Available subagents:
19
+ ${subagentList}
20
+ `;
21
+ }
22
+
23
+ /**
24
+ * Creates a Subagent tool configured with the available subagents.
25
+ *
26
+ * @param subagents - Array of subagent configurations (must have at least one)
27
+ * @returns A tool definition with dynamic schema based on available subagents
28
+ */
29
+ export function createSubagentTool<T extends SubagentConfig[]>(
30
+ subagents: T
31
+ ): {
32
+ readonly name: typeof SUBAGENT_TOOL_NAME;
33
+ readonly description: string;
34
+ readonly schema: z.ZodObject<z.ZodRawShape>;
35
+ } {
36
+ if (subagents.length === 0) {
37
+ throw new Error("createSubagentTool requires at least one subagent");
38
+ }
39
+
40
+ const names = subagents.map((s) => s.agentName);
41
+ const hasThreadContinuation = subagents.some(
42
+ (s) => s.allowThreadContinuation
43
+ );
44
+
45
+ const baseFields = {
46
+ subagent: z.enum(names).describe("The type of subagent to launch"),
47
+ description: z
48
+ .string()
49
+ .describe("A short (3-5 word) description of the task"),
50
+ prompt: z.string().describe("The task for the agent to perform"),
51
+ };
52
+
53
+ const schema = hasThreadContinuation
54
+ ? z.object({
55
+ ...baseFields,
56
+ threadId: z
57
+ .string()
58
+ .nullable()
59
+ .describe(
60
+ "Thread ID to continue an existing conversation, or null to start a new one"
61
+ ),
62
+ })
63
+ : z.object(baseFields);
64
+
65
+ return {
66
+ name: SUBAGENT_TOOL_NAME,
67
+ description: buildSubagentDescription(subagents),
68
+ schema,
69
+ } as const;
70
+ }
71
+
72
+ /**
73
+ * Subagent tool args type (when subagent names are not known at compile time)
74
+ */
75
+ export type SubagentArgs = {
76
+ subagent: string;
77
+ description: string;
78
+ prompt: string;
79
+ threadId?: string | null;
80
+ };
@@ -0,0 +1,92 @@
1
+ import type { z } from "zod";
2
+ import type {
3
+ ToolHandlerResponse,
4
+ PreToolUseHookResult,
5
+ PostToolUseFailureHookResult,
6
+ } from "../tool-router/types";
7
+
8
+ /** ToolHandlerResponse with threadId required (subagents must always surface their thread) */
9
+ export type SubagentHandlerResponse<TResult = null> =
10
+ ToolHandlerResponse<TResult> & { threadId: string };
11
+
12
+ export type SubagentWorkflow<TResult extends z.ZodType = z.ZodType> = (
13
+ input: SubagentInput
14
+ ) => Promise<SubagentHandlerResponse<z.infer<TResult> | null>>;
15
+
16
+ /** Infer the z.infer'd result type from a SubagentConfig, or null if no schema */
17
+ export type InferSubagentResult<T extends SubagentConfig> =
18
+ T extends SubagentConfig<infer S> ? z.infer<S> : null;
19
+
20
+ /**
21
+ * Configuration for a subagent that can be spawned by the parent workflow.
22
+ *
23
+ * @template TResult - Zod schema type for validating the child workflow's result
24
+ */
25
+ export interface SubagentConfig<TResult extends z.ZodType = z.ZodType> {
26
+ /** Identifier used in Task tool's subagent parameter */
27
+ agentName: string;
28
+ /** Description shown to the parent agent explaining what this subagent does */
29
+ description: string;
30
+ /** Whether this subagent is available (default: true). Disabled subagents are excluded from the Subagent tool. */
31
+ enabled?: boolean;
32
+ /** Temporal workflow function or type name (used with executeChild) */
33
+ workflow: string | SubagentWorkflow<TResult>;
34
+ /** Optional task queue - defaults to parent's queue if not specified */
35
+ taskQueue?: string;
36
+ /** Optional Zod schema to validate the child workflow's result. If omitted, result is passed through as-is. */
37
+ resultSchema?: TResult;
38
+ /** Optional static context passed to the subagent on every invocation */
39
+ context?: Record<string, unknown>;
40
+ /** Allow the parent agent to pass a threadId for this subagent to continue (default: false) */
41
+ allowThreadContinuation?: boolean;
42
+ /** Per-subagent lifecycle hooks */
43
+ hooks?: SubagentHooks;
44
+ /**
45
+ * Sandbox strategy for this subagent.
46
+ * - `'inherit'` (default): reuse the parent's sandbox (shared filesystem/exec).
47
+ * - `'own'`: the child creates and owns its own sandbox.
48
+ */
49
+ sandbox?: "inherit" | "own";
50
+ }
51
+
52
+ /**
53
+ * Per-subagent lifecycle hooks - defined on a SubagentConfig.
54
+ * Runs in addition to global hooks (global pre → subagent pre → execute → subagent post → global post).
55
+ */
56
+ export interface SubagentHooks<TArgs = unknown, TResult = unknown> {
57
+ /** Called before this subagent executes - can skip or modify args */
58
+ onPreExecution?: (ctx: {
59
+ args: TArgs;
60
+ threadId: string;
61
+ turn: number;
62
+ }) => PreToolUseHookResult | Promise<PreToolUseHookResult>;
63
+ /** Called after this subagent executes successfully */
64
+ onPostExecution?: (ctx: {
65
+ args: TArgs;
66
+ result: TResult;
67
+ threadId: string;
68
+ turn: number;
69
+ durationMs: number;
70
+ }) => void | Promise<void>;
71
+ /** Called when this subagent execution fails */
72
+ onExecutionFailure?: (ctx: {
73
+ args: TArgs;
74
+ error: Error;
75
+ threadId: string;
76
+ turn: number;
77
+ }) => PostToolUseFailureHookResult | Promise<PostToolUseFailureHookResult>;
78
+ }
79
+
80
+ /**
81
+ * Input passed to child workflows when spawned as subagents
82
+ */
83
+ export interface SubagentInput {
84
+ /** The prompt/task from the parent agent */
85
+ prompt: string;
86
+ /** Optional context parameters passed from the parent agent */
87
+ context?: Record<string, unknown>;
88
+ /** When set, the subagent should continue this thread instead of starting a new one */
89
+ previousThreadId?: string;
90
+ /** Sandbox ID inherited from the parent agent (when SubagentConfig.sandbox is 'inherit') */
91
+ sandboxId?: string;
92
+ }
@@ -0,0 +1,7 @@
1
+ export { createThreadManager } from "./manager";
2
+ export { getShortId } from "./id";
3
+
4
+ export type {
5
+ ThreadManagerConfig,
6
+ BaseThreadManager,
7
+ } from "./types";
@@ -1,4 +1,7 @@
1
- import type Redis from "ioredis";
1
+ import type {
2
+ ThreadManagerConfig,
3
+ BaseThreadManager,
4
+ } from "./types";
2
5
 
3
6
  const THREAD_TTL_SECONDS = 60 * 60 * 24 * 90; // 90 days
4
7
 
@@ -27,38 +30,6 @@ function getThreadKey(threadId: string, key: string): string {
27
30
  return `thread:${threadId}:${key}`;
28
31
  }
29
32
 
30
- export interface ThreadManagerConfig<T> {
31
- redis: Redis;
32
- threadId: string;
33
- /** Thread key, defaults to 'messages' */
34
- key?: string;
35
- /** Custom serializer, defaults to JSON.stringify */
36
- serialize?: (message: T) => string;
37
- /** Custom deserializer, defaults to JSON.parse */
38
- deserialize?: (raw: string) => T;
39
- /**
40
- * Extract a unique id from a message for idempotent appends.
41
- * When provided, `append` uses an atomic Lua script to skip duplicate writes.
42
- */
43
- idOf?: (message: T) => string;
44
- }
45
-
46
- /** Generic thread manager for any message type */
47
- export interface BaseThreadManager<T> {
48
- /** Initialize an empty thread */
49
- initialize(): Promise<void>;
50
- /** Load all messages from the thread */
51
- load(): Promise<T[]>;
52
- /**
53
- * Append messages to the thread.
54
- * When `idOf` is configured, appends are idempotent — retries with the
55
- * same message ids are atomically skipped via a Redis Lua script.
56
- */
57
- append(messages: T[]): Promise<void>;
58
- /** Delete the thread */
59
- delete(): Promise<void>;
60
- }
61
-
62
33
  /**
63
34
  * Creates a generic thread manager for handling conversation state in Redis.
64
35
  * Framework-agnostic — works with any serializable message type.
@@ -117,6 +88,22 @@ export function createThreadManager<T>(
117
88
  }
118
89
  },
119
90
 
91
+ async fork(newThreadId: string): Promise<BaseThreadManager<T>> {
92
+ await assertThreadExists();
93
+ const data = await redis.lrange(redisKey, 0, -1);
94
+ const forked = createThreadManager({
95
+ ...config,
96
+ threadId: newThreadId,
97
+ });
98
+ await forked.initialize();
99
+ if (data.length > 0) {
100
+ const newKey = getThreadKey(newThreadId, key);
101
+ await redis.rpush(newKey, ...data);
102
+ await redis.expire(newKey, THREAD_TTL_SECONDS);
103
+ }
104
+ return forked;
105
+ },
106
+
120
107
  async delete(): Promise<void> {
121
108
  await redis.del(redisKey, metaKey);
122
109
  },
@@ -0,0 +1,39 @@
1
+ import type Redis from "ioredis";
2
+
3
+ export interface ThreadManagerConfig<T> {
4
+ redis: Redis;
5
+ threadId: string;
6
+ /** Thread key, defaults to 'messages' */
7
+ key?: string;
8
+ /** Custom serializer, defaults to JSON.stringify */
9
+ serialize?: (message: T) => string;
10
+ /** Custom deserializer, defaults to JSON.parse */
11
+ deserialize?: (raw: string) => T;
12
+ /**
13
+ * Extract a unique id from a message for idempotent appends.
14
+ * When provided, `append` uses an atomic Lua script to skip duplicate writes.
15
+ */
16
+ idOf?: (message: T) => string;
17
+ }
18
+
19
+ /** Generic thread manager for any message type */
20
+ export interface BaseThreadManager<T> {
21
+ /** Initialize an empty thread */
22
+ initialize(): Promise<void>;
23
+ /** Load all messages from the thread */
24
+ load(): Promise<T[]>;
25
+ /**
26
+ * Append messages to the thread.
27
+ * When `idOf` is configured, appends are idempotent — retries with the
28
+ * same message ids are atomically skipped via a Redis Lua script.
29
+ */
30
+ append(messages: T[]): Promise<void>;
31
+ /**
32
+ * Copy all messages from this thread into a new thread, leaving the
33
+ * original intact. Returns the new thread's manager. Safe for parallel
34
+ * forks — each call creates an independent copy.
35
+ */
36
+ fork(newThreadId: string): Promise<BaseThreadManager<T>>;
37
+ /** Delete the thread */
38
+ delete(): Promise<void>;
39
+ }
@@ -0,0 +1,55 @@
1
+ import type { ToolResultConfig } from "../types";
2
+ import type { ActivityToolHandler, RouterContext } from "./types";
3
+
4
+ /**
5
+ * Wraps a tool handler to automatically append its result directly to the
6
+ * thread and sets `resultAppended: true` on the response.
7
+ *
8
+ * Use this for tools whose responses may exceed Temporal's activity payload
9
+ * limit. The wrapper appends to the thread inside the activity (where Redis
10
+ * is available), then replaces `toolResponse` with an empty string so the
11
+ * large payload never travels through the Temporal workflow boundary.
12
+ *
13
+ * @param getThread - Factory that returns a thread manager for the given threadId
14
+ * @param handler - The original tool handler
15
+ * @returns A wrapped handler that auto-appends and flags the response
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import { withAutoAppend } from '@bead-ai/zeitlich/workflow';
20
+ * import { createThreadManager } from '@bead-ai/zeitlich';
21
+ *
22
+ * const handler = withAutoAppend(
23
+ * (threadId) => createThreadManager({ redis, threadId }),
24
+ * async (args, ctx) => ({
25
+ * toolResponse: JSON.stringify(largeResult), // appended directly to Redis
26
+ * data: { summary: "..." }, // small data for workflow
27
+ * }),
28
+ * );
29
+ * ```
30
+ */
31
+ export function withAutoAppend<
32
+ TArgs,
33
+ TResult,
34
+ TContext extends RouterContext = RouterContext,
35
+ >(
36
+ threadHandler: (config: ToolResultConfig) => Promise<void>,
37
+ handler: ActivityToolHandler<TArgs, TResult, TContext>
38
+ ): ActivityToolHandler<TArgs, TResult, TContext> {
39
+ return async (args: TArgs, context: TContext) => {
40
+ const response = await handler(args, context);
41
+
42
+ await threadHandler({
43
+ threadId: context.threadId,
44
+ toolCallId: context.toolCallId,
45
+ toolName: context.toolName,
46
+ content: response.toolResponse,
47
+ });
48
+
49
+ return {
50
+ toolResponse: "Response appended via withAutoAppend",
51
+ data: response.data,
52
+ resultAppended: true,
53
+ };
54
+ };
55
+ }
@@ -0,0 +1,41 @@
1
+ export {
2
+ createToolRouter,
3
+ defineTool,
4
+ hasNoOtherToolCalls,
5
+ } from "./router";
6
+ export { withAutoAppend } from "./auto-append";
7
+ export { withSandbox } from "./with-sandbox";
8
+ export type { SandboxContext } from "./with-sandbox";
9
+
10
+ export type {
11
+ ToolDefinition,
12
+ ToolWithHandler,
13
+ ToolMap,
14
+ ToolNames,
15
+ RawToolCall,
16
+ ParsedToolCall,
17
+ ParsedToolCallUnion,
18
+ AppendToolResultFn,
19
+ ToolHandlerResponse,
20
+ RouterContext,
21
+ ToolHandler,
22
+ ActivityToolHandler,
23
+ ToolArgs,
24
+ ToolResult,
25
+ ToolCallResult,
26
+ InferToolResults,
27
+ ToolCallResultUnion,
28
+ ProcessToolCallsContext,
29
+ PreToolUseHookResult,
30
+ PostToolUseFailureHookResult,
31
+ ToolHooks,
32
+ PreToolUseHookContext,
33
+ PreToolUseHook,
34
+ PostToolUseHookContext,
35
+ PostToolUseHook,
36
+ PostToolUseFailureHookContext,
37
+ PostToolUseFailureHook,
38
+ ToolRouterHooks,
39
+ ToolRouterOptions,
40
+ ToolRouter,
41
+ } from "./types";