teleton 0.1.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.
@@ -0,0 +1,319 @@
1
+ // src/memory/agent/tasks.ts
2
+ import { randomUUID } from "crypto";
3
+ var TaskStore = class {
4
+ constructor(db) {
5
+ this.db = db;
6
+ }
7
+ /**
8
+ * Create a new task
9
+ */
10
+ createTask(task) {
11
+ const id = randomUUID();
12
+ const now = Math.floor(Date.now() / 1e3);
13
+ this.db.prepare(
14
+ `
15
+ INSERT INTO tasks (id, description, status, priority, created_by, created_at, scheduled_for, payload, reason, scheduled_message_id)
16
+ VALUES (?, ?, 'pending', ?, ?, ?, ?, ?, ?, ?)
17
+ `
18
+ ).run(
19
+ id,
20
+ task.description,
21
+ task.priority ?? 0,
22
+ task.createdBy ?? null,
23
+ now,
24
+ task.scheduledFor ? Math.floor(task.scheduledFor.getTime() / 1e3) : null,
25
+ task.payload ?? null,
26
+ task.reason ?? null,
27
+ task.scheduledMessageId ?? null
28
+ );
29
+ if (task.dependsOn && task.dependsOn.length > 0) {
30
+ for (const parentId of task.dependsOn) {
31
+ this.addDependency(id, parentId);
32
+ }
33
+ }
34
+ return {
35
+ id,
36
+ description: task.description,
37
+ status: "pending",
38
+ priority: task.priority ?? 0,
39
+ createdBy: task.createdBy,
40
+ createdAt: new Date(now * 1e3),
41
+ scheduledFor: task.scheduledFor,
42
+ payload: task.payload,
43
+ reason: task.reason,
44
+ scheduledMessageId: task.scheduledMessageId
45
+ };
46
+ }
47
+ /**
48
+ * Update a task
49
+ */
50
+ updateTask(taskId, updates) {
51
+ const task = this.getTask(taskId);
52
+ if (!task) return void 0;
53
+ const now = Math.floor(Date.now() / 1e3);
54
+ const updateFields = [];
55
+ const updateValues = [];
56
+ if (updates.description !== void 0) {
57
+ updateFields.push("description = ?");
58
+ updateValues.push(updates.description);
59
+ }
60
+ if (updates.status !== void 0) {
61
+ updateFields.push("status = ?");
62
+ updateValues.push(updates.status);
63
+ if (updates.status === "in_progress" && !task.startedAt) {
64
+ updateFields.push("started_at = ?");
65
+ updateValues.push(now);
66
+ }
67
+ if ((updates.status === "done" || updates.status === "failed" || updates.status === "cancelled") && !task.completedAt) {
68
+ updateFields.push("completed_at = ?");
69
+ updateValues.push(now);
70
+ }
71
+ }
72
+ if (updates.priority !== void 0) {
73
+ updateFields.push("priority = ?");
74
+ updateValues.push(updates.priority);
75
+ }
76
+ if (updates.result !== void 0) {
77
+ updateFields.push("result = ?");
78
+ updateValues.push(updates.result);
79
+ }
80
+ if (updates.error !== void 0) {
81
+ updateFields.push("error = ?");
82
+ updateValues.push(updates.error);
83
+ }
84
+ if (updateFields.length === 0) return task;
85
+ updateValues.push(taskId);
86
+ this.db.prepare(
87
+ `
88
+ UPDATE tasks
89
+ SET ${updateFields.join(", ")}
90
+ WHERE id = ?
91
+ `
92
+ ).run(...updateValues);
93
+ return this.getTask(taskId);
94
+ }
95
+ /**
96
+ * Get a task by ID
97
+ */
98
+ getTask(id) {
99
+ const row = this.db.prepare(`SELECT * FROM tasks WHERE id = ?`).get(id);
100
+ if (!row) return void 0;
101
+ return {
102
+ id: row.id,
103
+ description: row.description,
104
+ status: row.status,
105
+ priority: row.priority,
106
+ createdBy: row.created_by,
107
+ createdAt: new Date(row.created_at * 1e3),
108
+ startedAt: row.started_at ? new Date(row.started_at * 1e3) : void 0,
109
+ completedAt: row.completed_at ? new Date(row.completed_at * 1e3) : void 0,
110
+ result: row.result,
111
+ error: row.error,
112
+ scheduledFor: row.scheduled_for ? new Date(row.scheduled_for * 1e3) : void 0,
113
+ payload: row.payload,
114
+ reason: row.reason,
115
+ scheduledMessageId: row.scheduled_message_id
116
+ };
117
+ }
118
+ /**
119
+ * List tasks with optional filters
120
+ */
121
+ listTasks(filter) {
122
+ let sql = `SELECT * FROM tasks WHERE 1=1`;
123
+ const params = [];
124
+ if (filter?.status) {
125
+ sql += ` AND status = ?`;
126
+ params.push(filter.status);
127
+ }
128
+ if (filter?.createdBy) {
129
+ sql += ` AND created_by = ?`;
130
+ params.push(filter.createdBy);
131
+ }
132
+ sql += ` ORDER BY priority DESC, created_at ASC`;
133
+ const rows = this.db.prepare(sql).all(...params);
134
+ return rows.map((row) => ({
135
+ id: row.id,
136
+ description: row.description,
137
+ status: row.status,
138
+ priority: row.priority,
139
+ createdBy: row.created_by,
140
+ createdAt: new Date(row.created_at * 1e3),
141
+ startedAt: row.started_at ? new Date(row.started_at * 1e3) : void 0,
142
+ completedAt: row.completed_at ? new Date(row.completed_at * 1e3) : void 0,
143
+ result: row.result,
144
+ error: row.error,
145
+ scheduledFor: row.scheduled_for ? new Date(row.scheduled_for * 1e3) : void 0,
146
+ payload: row.payload,
147
+ reason: row.reason,
148
+ scheduledMessageId: row.scheduled_message_id
149
+ }));
150
+ }
151
+ /**
152
+ * Get active (pending or in_progress) tasks
153
+ */
154
+ getActiveTasks() {
155
+ const rows = this.db.prepare(
156
+ `
157
+ SELECT * FROM tasks
158
+ WHERE status IN ('pending', 'in_progress')
159
+ ORDER BY priority DESC, created_at ASC
160
+ `
161
+ ).all();
162
+ return rows.map((row) => ({
163
+ id: row.id,
164
+ description: row.description,
165
+ status: row.status,
166
+ priority: row.priority,
167
+ createdBy: row.created_by,
168
+ createdAt: new Date(row.created_at * 1e3),
169
+ startedAt: row.started_at ? new Date(row.started_at * 1e3) : void 0,
170
+ completedAt: row.completed_at ? new Date(row.completed_at * 1e3) : void 0,
171
+ result: row.result,
172
+ error: row.error,
173
+ scheduledFor: row.scheduled_for ? new Date(row.scheduled_for * 1e3) : void 0,
174
+ payload: row.payload,
175
+ reason: row.reason,
176
+ scheduledMessageId: row.scheduled_message_id
177
+ }));
178
+ }
179
+ /**
180
+ * Delete a task
181
+ */
182
+ deleteTask(taskId) {
183
+ const result = this.db.prepare(`DELETE FROM tasks WHERE id = ?`).run(taskId);
184
+ return result.changes > 0;
185
+ }
186
+ /**
187
+ * Mark task as done
188
+ */
189
+ completeTask(taskId, result) {
190
+ return this.updateTask(taskId, { status: "done", result });
191
+ }
192
+ /**
193
+ * Mark task as failed
194
+ */
195
+ failTask(taskId, error) {
196
+ return this.updateTask(taskId, { status: "failed", error });
197
+ }
198
+ /**
199
+ * Start a task
200
+ */
201
+ startTask(taskId) {
202
+ return this.updateTask(taskId, { status: "in_progress" });
203
+ }
204
+ /**
205
+ * Cancel a task
206
+ */
207
+ cancelTask(taskId) {
208
+ return this.updateTask(taskId, { status: "cancelled" });
209
+ }
210
+ /**
211
+ * Check if adding a dependency would create a cycle
212
+ * Uses BFS to traverse dependency graph
213
+ */
214
+ wouldCreateCycle(taskId, newParentId) {
215
+ if (taskId === newParentId) {
216
+ return true;
217
+ }
218
+ const visited = /* @__PURE__ */ new Set();
219
+ const queue = [newParentId];
220
+ while (queue.length > 0) {
221
+ const current = queue.shift();
222
+ if (current === taskId) {
223
+ return true;
224
+ }
225
+ if (visited.has(current)) {
226
+ continue;
227
+ }
228
+ visited.add(current);
229
+ const deps = this.getDependencies(current);
230
+ queue.push(...deps);
231
+ }
232
+ return false;
233
+ }
234
+ /**
235
+ * Add a dependency (taskId depends on parentTaskId)
236
+ * Throws error if would create circular dependency
237
+ */
238
+ addDependency(taskId, parentTaskId) {
239
+ if (this.wouldCreateCycle(taskId, parentTaskId)) {
240
+ throw new Error(
241
+ `Cannot add dependency: would create circular dependency (${taskId} \u2192 ${parentTaskId})`
242
+ );
243
+ }
244
+ this.db.prepare(`INSERT OR IGNORE INTO task_dependencies (task_id, depends_on_task_id) VALUES (?, ?)`).run(taskId, parentTaskId);
245
+ }
246
+ /**
247
+ * Get all tasks that this task depends on
248
+ */
249
+ getDependencies(taskId) {
250
+ const rows = this.db.prepare(`SELECT depends_on_task_id FROM task_dependencies WHERE task_id = ?`).all(taskId);
251
+ return rows.map((r) => r.depends_on_task_id);
252
+ }
253
+ /**
254
+ * Get all tasks that depend on this task
255
+ */
256
+ getDependents(taskId) {
257
+ const rows = this.db.prepare(`SELECT task_id FROM task_dependencies WHERE depends_on_task_id = ?`).all(taskId);
258
+ return rows.map((r) => r.task_id);
259
+ }
260
+ /**
261
+ * Check if a task can execute (all dependencies are done)
262
+ * Optimized: Single query with JOIN instead of N+1 queries
263
+ */
264
+ canExecute(taskId) {
265
+ const result = this.db.prepare(
266
+ `
267
+ SELECT COUNT(*) as pending_count
268
+ FROM task_dependencies td
269
+ LEFT JOIN tasks t ON td.depends_on_task_id = t.id
270
+ WHERE td.task_id = ?
271
+ AND (t.id IS NULL OR t.status != 'done')
272
+ `
273
+ ).get(taskId);
274
+ return result.pending_count === 0;
275
+ }
276
+ /**
277
+ * Get all parent task results for a dependent task
278
+ * Optimized: Single query with JOIN instead of N+1 queries
279
+ */
280
+ getParentResults(taskId) {
281
+ const rows = this.db.prepare(
282
+ `
283
+ SELECT t.id, t.description, t.result
284
+ FROM task_dependencies td
285
+ JOIN tasks t ON td.depends_on_task_id = t.id
286
+ WHERE td.task_id = ?
287
+ AND t.status = 'done'
288
+ AND t.result IS NOT NULL
289
+ `
290
+ ).all(taskId);
291
+ return rows.map((row) => {
292
+ let parsedResult;
293
+ try {
294
+ parsedResult = JSON.parse(row.result);
295
+ } catch (e) {
296
+ parsedResult = row.result;
297
+ }
298
+ return {
299
+ taskId: row.id,
300
+ description: row.description,
301
+ result: parsedResult
302
+ };
303
+ });
304
+ }
305
+ };
306
+ var instances = /* @__PURE__ */ new WeakMap();
307
+ function getTaskStore(db) {
308
+ let store = instances.get(db);
309
+ if (!store) {
310
+ store = new TaskStore(db);
311
+ instances.set(db, store);
312
+ }
313
+ return store;
314
+ }
315
+
316
+ export {
317
+ TaskStore,
318
+ getTaskStore
319
+ };