zeitlich 0.1.1 → 0.2.0

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 (58) hide show
  1. package/README.md +165 -180
  2. package/dist/index.cjs +1314 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +128 -0
  5. package/dist/index.d.ts +51 -75
  6. package/dist/index.js +741 -1091
  7. package/dist/index.js.map +1 -1
  8. package/dist/workflow-uVNF7zoe.d.cts +941 -0
  9. package/dist/workflow-uVNF7zoe.d.ts +941 -0
  10. package/dist/workflow.cjs +914 -0
  11. package/dist/workflow.cjs.map +1 -0
  12. package/dist/workflow.d.cts +5 -0
  13. package/dist/workflow.d.ts +2 -1
  14. package/dist/workflow.js +543 -423
  15. package/dist/workflow.js.map +1 -1
  16. package/package.json +19 -17
  17. package/src/activities.ts +112 -0
  18. package/src/index.ts +49 -0
  19. package/src/lib/fs.ts +80 -0
  20. package/src/lib/model-invoker.ts +75 -0
  21. package/src/lib/session.ts +216 -0
  22. package/src/lib/state-manager.ts +268 -0
  23. package/src/lib/thread-manager.ts +169 -0
  24. package/src/lib/tool-router.ts +717 -0
  25. package/src/lib/types.ts +354 -0
  26. package/src/plugin.ts +28 -0
  27. package/src/tools/ask-user-question/handler.ts +25 -0
  28. package/src/tools/ask-user-question/tool.ts +46 -0
  29. package/src/tools/bash/bash.test.ts +104 -0
  30. package/src/tools/bash/handler.ts +36 -0
  31. package/src/tools/bash/tool.ts +20 -0
  32. package/src/tools/edit/handler.ts +156 -0
  33. package/src/tools/edit/tool.ts +39 -0
  34. package/src/tools/glob/handler.ts +62 -0
  35. package/src/tools/glob/tool.ts +27 -0
  36. package/src/tools/grep/tool.ts +45 -0
  37. package/src/tools/read/tool.ts +33 -0
  38. package/src/tools/task/handler.ts +75 -0
  39. package/src/tools/task/tool.ts +96 -0
  40. package/src/tools/task-create/handler.ts +49 -0
  41. package/src/tools/task-create/tool.ts +66 -0
  42. package/src/tools/task-get/handler.ts +38 -0
  43. package/src/tools/task-get/tool.ts +11 -0
  44. package/src/tools/task-list/handler.ts +33 -0
  45. package/src/tools/task-list/tool.ts +9 -0
  46. package/src/tools/task-update/handler.ts +79 -0
  47. package/src/tools/task-update/tool.ts +20 -0
  48. package/src/tools/write/tool.ts +26 -0
  49. package/src/workflow.ts +138 -0
  50. package/tsup.config.ts +20 -0
  51. package/dist/index.d.mts +0 -152
  52. package/dist/index.mjs +0 -1587
  53. package/dist/index.mjs.map +0 -1
  54. package/dist/workflow-7_MT-5-w.d.mts +0 -1203
  55. package/dist/workflow-7_MT-5-w.d.ts +0 -1203
  56. package/dist/workflow.d.mts +0 -4
  57. package/dist/workflow.mjs +0 -739
  58. package/dist/workflow.mjs.map +0 -1
@@ -0,0 +1,268 @@
1
+ import { defineQuery, setHandler } from "@temporalio/workflow";
2
+ import {
3
+ type AgentStatus,
4
+ type BaseAgentState,
5
+ type WorkflowTask,
6
+ isTerminalStatus,
7
+ } from "./types";
8
+ import type { ToolDefinition } from "./tool-router";
9
+
10
+ /**
11
+ * JSON primitive types that Temporal can serialize
12
+ */
13
+ export type JsonPrimitive = string | number | boolean | null | undefined;
14
+
15
+ /**
16
+ * JSON-serializable value (recursive type for Temporal compatibility)
17
+ */
18
+ export type JsonValue =
19
+ | JsonPrimitive
20
+ | JsonValue[]
21
+ | { [key: string]: JsonValue };
22
+
23
+ /**
24
+ * Type constraint ensuring T only contains JSON-serializable values.
25
+ * Use this for custom state to ensure Temporal workflow compatibility.
26
+ *
27
+ * Allows: primitives, arrays, plain objects, and JsonValue
28
+ * Rejects: functions, symbols, undefined, class instances with methods
29
+ */
30
+ export type JsonSerializable<T> = {
31
+ [K in keyof T]: T[K] extends JsonValue
32
+ ? T[K]
33
+ : T[K] extends JsonPrimitive
34
+ ? T[K]
35
+ : T[K] extends (infer U)[]
36
+ ? U extends JsonValue
37
+ ? T[K]
38
+ : JsonSerializable<U>[]
39
+ : T[K] extends object
40
+ ? JsonSerializable<T[K]>
41
+ : never;
42
+ };
43
+
44
+ /**
45
+ * Full state type combining base state with custom state
46
+ */
47
+ export type AgentState<TCustom extends JsonSerializable<TCustom>> =
48
+ BaseAgentState & TCustom;
49
+
50
+ /**
51
+ * Agent state manager interface
52
+ * Note: Temporal handlers must be set up in the workflow file due to
53
+ * Temporal's workflow isolation requirements. This manager provides
54
+ * the state and helpers needed for those handlers.
55
+ */
56
+ export interface AgentStateManager<TCustom extends JsonSerializable<TCustom>> {
57
+ /** Get current status */
58
+ getStatus(): AgentStatus;
59
+ /** Check if agent is running */
60
+ isRunning(): boolean;
61
+ /** Check if agent is in terminal state */
62
+ isTerminal(): boolean;
63
+ /** Get current state version */
64
+ getVersion(): number;
65
+
66
+ /** Set status to RUNNING */
67
+ run(): void;
68
+ /** Set status to WAITING_FOR_INPUT */
69
+ waitForInput(): void;
70
+ /** Set status to COMPLETED */
71
+ complete(): void;
72
+ /** Set status to FAILED */
73
+ fail(): void;
74
+ /** Set status to CANCELLED */
75
+ cancel(): void;
76
+
77
+ /** Increment state version (call after state changes) */
78
+ incrementVersion(): void;
79
+
80
+ /** Increment turns (call after each turn) */
81
+ incrementTurns(): void;
82
+
83
+ /** Get current turns */
84
+ getTurns(): number;
85
+
86
+ /** Get a custom state value by key */
87
+ get<K extends keyof TCustom>(key: K): TCustom[K];
88
+
89
+ /** Set a custom state value by key */
90
+ set<K extends keyof TCustom>(key: K, value: TCustom[K]): void;
91
+
92
+ /** Get full state for query handler */
93
+ getCurrentState(): AgentState<TCustom>;
94
+
95
+ /** Check if should return from waitForStateChange */
96
+ shouldReturnFromWait(lastKnownVersion: number): boolean;
97
+
98
+ // Task management methods
99
+ /** Get all tasks */
100
+ getTasks(): WorkflowTask[];
101
+ /** Get a task by ID */
102
+ getTask(id: string): WorkflowTask | undefined;
103
+ /** Add or update a task */
104
+ setTask(task: WorkflowTask): void;
105
+ /** Delete a task by ID */
106
+ deleteTask(id: string): boolean;
107
+
108
+ /** Set the tools */
109
+ setTools(newTools: ToolDefinition[]): void;
110
+ }
111
+
112
+ export const getStateQuery = defineQuery<BaseAgentState>("getState");
113
+
114
+ /**
115
+ * Creates an agent state manager for tracking workflow state.
116
+ *
117
+ * @param initialState - Optional initial values for base and custom state
118
+ * Base state defaults: status="RUNNING", version=0, turns=0, tasks=empty, fileTree=[]
119
+ *
120
+ * Note: Due to Temporal's workflow isolation, handlers must be set up
121
+ * in the workflow file using defineQuery/defineUpdate and setHandler.
122
+ * This manager provides the state and logic needed for those handlers.
123
+ */
124
+ export function createAgentStateManager<
125
+ TCustom extends JsonSerializable<TCustom> = Record<string, never>,
126
+ >(
127
+ initialState?: Partial<BaseAgentState> & TCustom
128
+ ): AgentStateManager<TCustom> {
129
+ // Default state (BaseAgentState fields)
130
+ let status: AgentStatus = initialState?.status ?? "RUNNING";
131
+ let version = initialState?.version ?? 0;
132
+ let turns = initialState?.turns ?? 0;
133
+ let tools = initialState?.tools ?? [];
134
+
135
+ // Tasks state
136
+ const tasks = new Map<string, WorkflowTask>(initialState?.tasks);
137
+
138
+ // Custom state - extract only custom fields (exclude base state keys)
139
+ const {
140
+ status: _,
141
+ version: __,
142
+ turns: ___,
143
+ tasks: ____,
144
+ tools: _____,
145
+ ...custom
146
+ } = initialState ?? {};
147
+ const customState = custom as TCustom;
148
+
149
+ function buildState(): AgentState<TCustom> {
150
+ return {
151
+ status,
152
+ version,
153
+ turns,
154
+ tools,
155
+ ...customState,
156
+ } as AgentState<TCustom>;
157
+ }
158
+
159
+ setHandler(getStateQuery, () => {
160
+ return buildState();
161
+ });
162
+
163
+ return {
164
+ getStatus(): AgentStatus {
165
+ return status;
166
+ },
167
+
168
+ isRunning(): boolean {
169
+ return status === "RUNNING";
170
+ },
171
+
172
+ isTerminal(): boolean {
173
+ return isTerminalStatus(status);
174
+ },
175
+
176
+ getTurns(): number {
177
+ return turns;
178
+ },
179
+
180
+ getVersion(): number {
181
+ return version;
182
+ },
183
+
184
+ run(): void {
185
+ status = "RUNNING";
186
+ version++;
187
+ },
188
+
189
+ waitForInput(): void {
190
+ status = "WAITING_FOR_INPUT";
191
+ version++;
192
+ },
193
+
194
+ complete(): void {
195
+ status = "COMPLETED";
196
+ version++;
197
+ },
198
+
199
+ fail(): void {
200
+ status = "FAILED";
201
+ version++;
202
+ },
203
+
204
+ cancel(): void {
205
+ status = "CANCELLED";
206
+ version++;
207
+ },
208
+
209
+ incrementVersion(): void {
210
+ version++;
211
+ },
212
+
213
+ incrementTurns(): void {
214
+ turns++;
215
+ },
216
+
217
+ get<K extends keyof TCustom>(key: K): TCustom[K] {
218
+ return customState[key];
219
+ },
220
+
221
+ set<K extends keyof TCustom>(key: K, value: TCustom[K]): void {
222
+ customState[key] = value;
223
+ version++;
224
+ },
225
+
226
+ getCurrentState(): AgentState<TCustom> {
227
+ return buildState();
228
+ },
229
+
230
+ shouldReturnFromWait(lastKnownVersion: number): boolean {
231
+ return version > lastKnownVersion || isTerminalStatus(status);
232
+ },
233
+
234
+ getTasks(): WorkflowTask[] {
235
+ return Array.from(tasks.values());
236
+ },
237
+
238
+ getTask(id: string): WorkflowTask | undefined {
239
+ return tasks.get(id);
240
+ },
241
+
242
+ setTask(task: WorkflowTask): void {
243
+ tasks.set(task.id, task);
244
+ version++;
245
+ },
246
+
247
+ setTools(newTools: ToolDefinition[]): void {
248
+ tools = newTools;
249
+ },
250
+
251
+ deleteTask(id: string): boolean {
252
+ const deleted = tasks.delete(id);
253
+ if (deleted) {
254
+ version++;
255
+ }
256
+ return deleted;
257
+ },
258
+ };
259
+ }
260
+
261
+ /**
262
+ * Handler names used across agents
263
+ */
264
+ export const AGENT_HANDLER_NAMES = {
265
+ getAgentState: "getAgentState",
266
+ waitForStateChange: "waitForStateChange",
267
+ addMessage: "addMessage",
268
+ } as const;
@@ -0,0 +1,169 @@
1
+ import type Redis from "ioredis";
2
+
3
+ import {
4
+ type $InferMessageContent,
5
+ AIMessage,
6
+ HumanMessage,
7
+ type MessageContent,
8
+ type MessageStructure,
9
+ type StoredMessage,
10
+ SystemMessage,
11
+ ToolMessage,
12
+ } from "@langchain/core/messages";
13
+ import { v4 as uuidv4 } from "uuid";
14
+
15
+ const THREAD_TTL_SECONDS = 60 * 60 * 24 * 90; // 90 days
16
+
17
+ function getThreadKey(threadId: string, key: string): string {
18
+ return `thread:${threadId}:${key}`;
19
+ }
20
+
21
+ /**
22
+ * Content for a tool message response.
23
+ * Can be a simple string or complex content parts (text, images, cache points, etc.)
24
+ */
25
+ export type ToolMessageContent = $InferMessageContent<MessageStructure, "tool">;
26
+
27
+ export interface ThreadManagerConfig {
28
+ redis: Redis;
29
+ threadId: string;
30
+ /** Thread key, defaults to 'messages' */
31
+ key?: string;
32
+ }
33
+
34
+ export interface ThreadManager {
35
+ /** Append a system message to the thread */
36
+ appendSystemMessage(content: string): Promise<void>;
37
+ /** Initialize an empty thread */
38
+ initialize(): Promise<void>;
39
+ /** Load all messages from the thread */
40
+ load(): Promise<StoredMessage[]>;
41
+ /** Append messages to the thread */
42
+ append(messages: StoredMessage[]): Promise<void>;
43
+ /** Delete the thread */
44
+ delete(): Promise<void>;
45
+
46
+ /** Create a SystemMessage (returns StoredMessage for storage) */
47
+ createSystemMessage(content: string): StoredMessage;
48
+
49
+ /** Create a HumanMessage (returns StoredMessage for storage) */
50
+ createHumanMessage(content: string | MessageContent): StoredMessage;
51
+
52
+ /** Create an AIMessage with optional additional kwargs */
53
+ createAIMessage(
54
+ content: string | MessageContent,
55
+ kwargs?: { header?: string; options?: string[]; multiSelect?: boolean }
56
+ ): StoredMessage;
57
+
58
+ /** Create a ToolMessage */
59
+ createToolMessage(
60
+ content: ToolMessageContent,
61
+ toolCallId: string
62
+ ): StoredMessage;
63
+
64
+ /** Create and append a HumanMessage */
65
+ appendHumanMessage(content: string | MessageContent): Promise<void>;
66
+ /** Create and append a ToolMessage */
67
+ appendToolMessage(
68
+ content: ToolMessageContent,
69
+ toolCallId: string
70
+ ): Promise<void>;
71
+ /** Create and append an AIMessage */
72
+ appendAIMessage(content: string | MessageContent): Promise<void>;
73
+ }
74
+
75
+ /**
76
+ * Creates a thread manager for handling conversation state in Redis.
77
+ */
78
+ export function createThreadManager(
79
+ config: ThreadManagerConfig
80
+ ): ThreadManager {
81
+ const { redis, threadId, key = "messages" } = config;
82
+ const redisKey = getThreadKey(threadId, key);
83
+
84
+ return {
85
+ async initialize(): Promise<void> {
86
+ await redis.del(redisKey);
87
+ },
88
+
89
+ async load(): Promise<StoredMessage[]> {
90
+ const data = await redis.lrange(redisKey, 0, -1);
91
+ return data.map((item) => JSON.parse(item) as StoredMessage);
92
+ },
93
+
94
+ async append(messages: StoredMessage[]): Promise<void> {
95
+ if (messages.length > 0) {
96
+ await redis.rpush(redisKey, ...messages.map((m) => JSON.stringify(m)));
97
+ await redis.expire(redisKey, THREAD_TTL_SECONDS);
98
+ }
99
+ },
100
+
101
+ async delete(): Promise<void> {
102
+ await redis.del(redisKey);
103
+ },
104
+
105
+ createHumanMessage(content: string | MessageContent): StoredMessage {
106
+ return new HumanMessage({
107
+ id: uuidv4(),
108
+ content: content as string,
109
+ }).toDict();
110
+ },
111
+
112
+ createAIMessage(
113
+ content: string,
114
+ kwargs?: { header?: string; options?: string[]; multiSelect?: boolean }
115
+ ): StoredMessage {
116
+ return new AIMessage({
117
+ id: uuidv4(),
118
+ content,
119
+ additional_kwargs: kwargs
120
+ ? {
121
+ header: kwargs.header,
122
+ options: kwargs.options,
123
+ multiSelect: kwargs.multiSelect,
124
+ }
125
+ : undefined,
126
+ }).toDict();
127
+ },
128
+
129
+ createToolMessage(
130
+ content: ToolMessageContent,
131
+ toolCallId: string
132
+ ): StoredMessage {
133
+ return new ToolMessage({
134
+ // Cast needed due to langchain type compatibility
135
+ content: content as MessageContent,
136
+ tool_call_id: toolCallId,
137
+ }).toDict();
138
+ },
139
+
140
+ createSystemMessage(content: string): StoredMessage {
141
+ return new SystemMessage({
142
+ content,
143
+ }).toDict();
144
+ },
145
+
146
+ async appendSystemMessage(content: string): Promise<void> {
147
+ const message = this.createSystemMessage(content);
148
+ await this.append([message]);
149
+ },
150
+
151
+ async appendHumanMessage(content: string | MessageContent): Promise<void> {
152
+ const message = this.createHumanMessage(content);
153
+ await this.append([message]);
154
+ },
155
+
156
+ async appendToolMessage(
157
+ content: ToolMessageContent,
158
+ toolCallId: string
159
+ ): Promise<void> {
160
+ const message = this.createToolMessage(content, toolCallId);
161
+ await this.append([message]);
162
+ },
163
+
164
+ async appendAIMessage(content: string | MessageContent): Promise<void> {
165
+ const message = this.createAIMessage(content);
166
+ await this.append([message]);
167
+ },
168
+ };
169
+ }