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.
- package/README.md +165 -180
- package/dist/index.cjs +1314 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +128 -0
- package/dist/index.d.ts +51 -75
- package/dist/index.js +741 -1091
- package/dist/index.js.map +1 -1
- package/dist/workflow-uVNF7zoe.d.cts +941 -0
- package/dist/workflow-uVNF7zoe.d.ts +941 -0
- package/dist/workflow.cjs +914 -0
- package/dist/workflow.cjs.map +1 -0
- package/dist/workflow.d.cts +5 -0
- package/dist/workflow.d.ts +2 -1
- package/dist/workflow.js +543 -423
- package/dist/workflow.js.map +1 -1
- package/package.json +19 -17
- package/src/activities.ts +112 -0
- package/src/index.ts +49 -0
- package/src/lib/fs.ts +80 -0
- package/src/lib/model-invoker.ts +75 -0
- package/src/lib/session.ts +216 -0
- package/src/lib/state-manager.ts +268 -0
- package/src/lib/thread-manager.ts +169 -0
- package/src/lib/tool-router.ts +717 -0
- package/src/lib/types.ts +354 -0
- package/src/plugin.ts +28 -0
- package/src/tools/ask-user-question/handler.ts +25 -0
- package/src/tools/ask-user-question/tool.ts +46 -0
- package/src/tools/bash/bash.test.ts +104 -0
- package/src/tools/bash/handler.ts +36 -0
- package/src/tools/bash/tool.ts +20 -0
- package/src/tools/edit/handler.ts +156 -0
- package/src/tools/edit/tool.ts +39 -0
- package/src/tools/glob/handler.ts +62 -0
- package/src/tools/glob/tool.ts +27 -0
- package/src/tools/grep/tool.ts +45 -0
- package/src/tools/read/tool.ts +33 -0
- package/src/tools/task/handler.ts +75 -0
- package/src/tools/task/tool.ts +96 -0
- package/src/tools/task-create/handler.ts +49 -0
- package/src/tools/task-create/tool.ts +66 -0
- package/src/tools/task-get/handler.ts +38 -0
- package/src/tools/task-get/tool.ts +11 -0
- package/src/tools/task-list/handler.ts +33 -0
- package/src/tools/task-list/tool.ts +9 -0
- package/src/tools/task-update/handler.ts +79 -0
- package/src/tools/task-update/tool.ts +20 -0
- package/src/tools/write/tool.ts +26 -0
- package/src/workflow.ts +138 -0
- package/tsup.config.ts +20 -0
- package/dist/index.d.mts +0 -152
- package/dist/index.mjs +0 -1587
- package/dist/index.mjs.map +0 -1
- package/dist/workflow-7_MT-5-w.d.mts +0 -1203
- package/dist/workflow-7_MT-5-w.d.ts +0 -1203
- package/dist/workflow.d.mts +0 -4
- package/dist/workflow.mjs +0 -739
- 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
|
+
}
|