@ai.ntellect/core 0.3.0 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- package/agent/index.ts +68 -19
- package/dist/agent/index.d.ts +5 -1
- package/dist/agent/index.js +25 -9
- package/dist/llm/evaluator/context.js +7 -5
- package/dist/llm/evaluator/index.js +8 -9
- package/dist/llm/interpreter/context.js +1 -1
- package/dist/llm/orchestrator/context.d.ts +1 -1
- package/dist/llm/orchestrator/context.js +4 -6
- package/dist/llm/orchestrator/index.js +1 -1
- package/dist/memory/cache.d.ts +0 -1
- package/dist/memory/cache.js +4 -21
- package/dist/memory/persistent.d.ts +4 -10
- package/dist/memory/persistent.js +17 -53
- package/dist/services/scheduler.d.ts +17 -0
- package/dist/services/scheduler.js +121 -0
- package/dist/services/telegram-monitor.d.ts +15 -0
- package/dist/services/telegram-monitor.js +102 -0
- package/dist/test.d.ts +0 -167
- package/dist/test.js +503 -372
- package/dist/types.d.ts +22 -2
- package/llm/evaluator/context.ts +7 -5
- package/llm/evaluator/index.ts +9 -10
- package/llm/interpreter/context.ts +1 -1
- package/llm/orchestrator/context.ts +4 -6
- package/llm/orchestrator/index.ts +1 -1
- package/memory/cache.ts +4 -24
- package/memory/persistent.ts +17 -73
- package/package.json +1 -1
- package/services/scheduler.ts +167 -0
- package/services/telegram-monitor.ts +138 -0
- package/types.ts +24 -2
package/dist/types.d.ts
CHANGED
@@ -154,8 +154,7 @@ export interface Memory {
|
|
154
154
|
query: string;
|
155
155
|
purpose: string;
|
156
156
|
data: any;
|
157
|
-
|
158
|
-
userId?: string;
|
157
|
+
roomId: string;
|
159
158
|
createdAt: Date;
|
160
159
|
chunks?: MemoryChunk[];
|
161
160
|
}
|
@@ -205,3 +204,24 @@ export interface TransformedQueueItem {
|
|
205
204
|
name: string;
|
206
205
|
parameters: QueueItemParameter[];
|
207
206
|
}
|
207
|
+
export interface ScheduledAction {
|
208
|
+
id: string;
|
209
|
+
action: {
|
210
|
+
name: string;
|
211
|
+
parameters: QueueItemParameter[];
|
212
|
+
};
|
213
|
+
scheduledTime: Date;
|
214
|
+
userId: string;
|
215
|
+
status: "pending" | "completed" | "failed";
|
216
|
+
recurrence?: {
|
217
|
+
type: "daily" | "weekly" | "monthly";
|
218
|
+
interval: number;
|
219
|
+
};
|
220
|
+
}
|
221
|
+
export interface ScheduledActionEvents {
|
222
|
+
onActionStart?: (action: ScheduledAction) => void;
|
223
|
+
onActionComplete?: (action: ScheduledAction, result: any) => void;
|
224
|
+
onActionFailed?: (action: ScheduledAction, error: Error) => void;
|
225
|
+
onActionScheduled?: (action: ScheduledAction) => void;
|
226
|
+
onActionCancelled?: (actionId: string) => void;
|
227
|
+
}
|
package/llm/evaluator/context.ts
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
export const evaluatorContext = {
|
2
2
|
behavior: {
|
3
|
-
language: "
|
4
|
-
role: "Your role is to
|
3
|
+
language: "same_as_user",
|
4
|
+
role: "Your role is to ensure the goal will be achieved and make a response or suggest next actions.",
|
5
5
|
guidelines: {
|
6
6
|
important: [
|
7
|
-
"Verify if all
|
8
|
-
"Check if the results align with the initial goal.",
|
7
|
+
"Verify if all actions were executed successfully (actionsAlreadyDone).",
|
8
|
+
"Check if the results align with the initial goal (explain in 'why' field).",
|
9
|
+
"Suggest next actions in 'nextActionsNeeded' if the goal is not achieved and if actions in 'actionsAlreadyDone' are not enough.",
|
9
10
|
"If you retrieved the informations from your internal knowledge base, no need to store them in 'extraInformationsToStore'.",
|
10
|
-
"Store ONLY
|
11
|
+
"Store ONLY new needed informations in 'extraInformationsToStore'.",
|
12
|
+
"Choose the most relevant informations and memory type: episodic, semantic, or procedural.",
|
11
13
|
],
|
12
14
|
warnings: [
|
13
15
|
"NEVER store an old data you retrieve from your internal knowledge base.",
|
package/llm/evaluator/index.ts
CHANGED
@@ -3,7 +3,7 @@ import { generateObject } from "ai";
|
|
3
3
|
import { z } from "zod";
|
4
4
|
import { CacheMemory } from "../../memory/cache";
|
5
5
|
import { PersistentMemory } from "../../memory/persistent";
|
6
|
-
import { ActionSchema,
|
6
|
+
import { ActionSchema, MemoryType, State } from "../../types";
|
7
7
|
import { injectActions } from "../../utils/inject-actions";
|
8
8
|
import { Interpreter } from "../interpreter";
|
9
9
|
import { evaluatorContext } from "./context";
|
@@ -62,8 +62,8 @@ export class Evaluator {
|
|
62
62
|
const response = await generateObject({
|
63
63
|
model: this.model,
|
64
64
|
schema: z.object({
|
65
|
-
|
66
|
-
|
65
|
+
requestLanguage: z.string(),
|
66
|
+
actionsAlreadyDone: z.array(z.string()),
|
67
67
|
extraInformationsToStore: z.array(
|
68
68
|
z.object({
|
69
69
|
memoryType: z.enum(["episodic", "semantic", "procedural"]),
|
@@ -111,7 +111,7 @@ export class Evaluator {
|
|
111
111
|
console.log("Type:", item.memoryType);
|
112
112
|
console.log("Content:", item.queryForData);
|
113
113
|
|
114
|
-
const memories = await this.memory.persistent.
|
114
|
+
const memories = await this.memory.persistent.findRelevantDocuments(
|
115
115
|
item.queryForData,
|
116
116
|
{
|
117
117
|
similarityThreshold: 70,
|
@@ -129,7 +129,7 @@ export class Evaluator {
|
|
129
129
|
purpose: item.memoryType,
|
130
130
|
query: item.queryForData,
|
131
131
|
data: item.data,
|
132
|
-
|
132
|
+
roomId: "global",
|
133
133
|
createdAt: new Date(),
|
134
134
|
});
|
135
135
|
}
|
@@ -141,13 +141,12 @@ export class Evaluator {
|
|
141
141
|
cacheMemory.createMemory({
|
142
142
|
content: prompt,
|
143
143
|
type: MemoryType.ACTION,
|
144
|
-
data: validatedResponse.
|
145
|
-
scope: MemoryScope.GLOBAL,
|
144
|
+
data: validatedResponse.actionsAlreadyDone,
|
146
145
|
});
|
147
146
|
console.log(
|
148
147
|
"✅ Workflow actions completed stored in cache",
|
149
148
|
prompt,
|
150
|
-
validatedResponse.
|
149
|
+
validatedResponse.actionsAlreadyDone
|
151
150
|
);
|
152
151
|
}
|
153
152
|
console.log("\n✅ Evaluation completed");
|
@@ -164,7 +163,7 @@ export class Evaluator {
|
|
164
163
|
if (error.value.extraInformationsToStore.length > 0) {
|
165
164
|
for (const item of error.value.extraInformationsToStore) {
|
166
165
|
// Check if the item is already in the memory
|
167
|
-
const memories = await this.memory.persistent.
|
166
|
+
const memories = await this.memory.persistent.findRelevantDocuments(
|
168
167
|
item.content
|
169
168
|
);
|
170
169
|
if (memories.length === 0) {
|
@@ -177,7 +176,7 @@ export class Evaluator {
|
|
177
176
|
purpose: "importantToRemember",
|
178
177
|
query: item.content,
|
179
178
|
data: item.data,
|
180
|
-
|
179
|
+
roomId: "global",
|
181
180
|
createdAt: new Date(),
|
182
181
|
});
|
183
182
|
}
|
@@ -1,17 +1,15 @@
|
|
1
1
|
export const orchestratorContext = {
|
2
2
|
behavior: {
|
3
|
-
language: "
|
4
|
-
role: "
|
3
|
+
language: "same_as_user",
|
4
|
+
role: "Your role is to determine what actions are needed to achieve the user goal.",
|
5
5
|
guidelines: {
|
6
6
|
important: [
|
7
7
|
"If there is no action to do, you must answer in the 'answer' field.",
|
8
8
|
"If some parameters are not clear or missing, don't add the action, YOU MUST ask the user for them.",
|
9
|
-
"
|
9
|
+
"For QUESTIONS or ANALYSIS, search first in your internal knowledge base before using actions.",
|
10
10
|
"For ON-CHAIN actions, just use the useful actions.",
|
11
|
-
"For QUESTIONS or ANALYSIS, you MUST search in your cache memory or/and internal knowledge base.",
|
12
|
-
"NEVER repeat same actions if the user doesn't ask for it.",
|
13
11
|
],
|
14
|
-
warnings: [],
|
12
|
+
warnings: ["NEVER repeat same actions if the user doesn't ask for it."],
|
15
13
|
},
|
16
14
|
},
|
17
15
|
};
|
@@ -41,7 +41,7 @@ export class Orchestrator {
|
|
41
41
|
}),
|
42
42
|
execute: async ({ query }: { query: string }) => {
|
43
43
|
const persistentMemories =
|
44
|
-
await this.memory.persistent.
|
44
|
+
await this.memory.persistent.findRelevantDocuments(query, {
|
45
45
|
similarityThreshold: 70,
|
46
46
|
});
|
47
47
|
return `# LONG_TERM_MEMORY: ${JSON.stringify(persistentMemories)}`;
|
package/memory/cache.ts
CHANGED
@@ -44,15 +44,8 @@ export class CacheMemory {
|
|
44
44
|
}
|
45
45
|
}
|
46
46
|
|
47
|
-
private getMemoryKey(scope: MemoryScope, userId?: string): string {
|
48
|
-
if (scope === MemoryScope.GLOBAL) {
|
49
|
-
return `${this.CACHE_PREFIX}global:`;
|
50
|
-
}
|
51
|
-
return `${this.CACHE_PREFIX}user:${userId}:`;
|
52
|
-
}
|
53
|
-
|
54
47
|
private async storeMemory(memory: CacheMemoryType) {
|
55
|
-
const prefix = this.
|
48
|
+
const prefix = this.CACHE_PREFIX;
|
56
49
|
const key = `${prefix}${memory.id}`;
|
57
50
|
const result = await this.redis.set(key, JSON.stringify(memory), {
|
58
51
|
EX: this.CACHE_TTL,
|
@@ -124,23 +117,10 @@ export class CacheMemory {
|
|
124
117
|
scope?: MemoryScope,
|
125
118
|
userId?: string
|
126
119
|
): Promise<CacheMemoryType[]> {
|
127
|
-
|
128
|
-
|
129
|
-
if (!scope || scope === MemoryScope.GLOBAL) {
|
130
|
-
const globalPrefix = this.getMemoryKey(MemoryScope.GLOBAL);
|
131
|
-
const globalKeys = await this.redis.keys(`${globalPrefix}*`);
|
132
|
-
const globalPatterns = await this.getMemoriesFromKeys(globalKeys);
|
133
|
-
patterns = patterns.concat(globalPatterns);
|
134
|
-
}
|
135
|
-
|
136
|
-
if (userId && (!scope || scope === MemoryScope.USER)) {
|
137
|
-
const userPrefix = this.getMemoryKey(MemoryScope.USER, userId);
|
138
|
-
const userKeys = await this.redis.keys(`${userPrefix}*`);
|
139
|
-
const userPatterns = await this.getMemoriesFromKeys(userKeys);
|
140
|
-
patterns = patterns.concat(userPatterns);
|
141
|
-
}
|
120
|
+
const keys = await this.redis.keys(`${this.CACHE_PREFIX}*`);
|
121
|
+
const memories = await this.getMemoriesFromKeys(keys);
|
142
122
|
|
143
|
-
return
|
123
|
+
return memories;
|
144
124
|
}
|
145
125
|
|
146
126
|
private async getMemoriesFromKeys(
|
package/memory/persistent.ts
CHANGED
@@ -28,18 +28,6 @@ interface MeilisearchResponse {
|
|
28
28
|
}>;
|
29
29
|
}
|
30
30
|
|
31
|
-
interface SearchParams {
|
32
|
-
q?: string;
|
33
|
-
offset?: number;
|
34
|
-
limit?: number;
|
35
|
-
filter?: string | string[];
|
36
|
-
facets?: string[];
|
37
|
-
attributesToRetrieve?: string[];
|
38
|
-
attributesToSearchOn?: string[];
|
39
|
-
sort?: string[];
|
40
|
-
matchingStrategy?: "last" | "all" | "frequency";
|
41
|
-
}
|
42
|
-
|
43
31
|
interface ProcessedChunk {
|
44
32
|
content: string;
|
45
33
|
embedding: number[];
|
@@ -56,7 +44,7 @@ export class PersistentMemory {
|
|
56
44
|
constructor(options: { host: string; apiKey: string; indexPrefix?: string }) {
|
57
45
|
this.host = options.host;
|
58
46
|
this.apiKey = options.apiKey;
|
59
|
-
this.INDEX_PREFIX = options.indexPrefix || "
|
47
|
+
this.INDEX_PREFIX = options.indexPrefix || "memory";
|
60
48
|
}
|
61
49
|
|
62
50
|
/**
|
@@ -64,10 +52,7 @@ export class PersistentMemory {
|
|
64
52
|
*/
|
65
53
|
async init() {
|
66
54
|
// Create global index
|
67
|
-
await this._getOrCreateIndex(this.
|
68
|
-
|
69
|
-
// Create user index
|
70
|
-
await this._getOrCreateIndex(this._getIndexName(MemoryScope.USER));
|
55
|
+
await this._getOrCreateIndex(this.INDEX_PREFIX);
|
71
56
|
}
|
72
57
|
|
73
58
|
/**
|
@@ -94,17 +79,6 @@ export class PersistentMemory {
|
|
94
79
|
|
95
80
|
return response.json() as Promise<T>;
|
96
81
|
}
|
97
|
-
|
98
|
-
/**
|
99
|
-
* Get index name based on scope and userId
|
100
|
-
*/
|
101
|
-
private _getIndexName(scope: MemoryScope, userId?: string): string {
|
102
|
-
if (scope === "global") {
|
103
|
-
return `${this.INDEX_PREFIX}global`;
|
104
|
-
}
|
105
|
-
return `${this.INDEX_PREFIX}user_${userId}`;
|
106
|
-
}
|
107
|
-
|
108
82
|
/**
|
109
83
|
* Get or create an index with proper settings
|
110
84
|
*/
|
@@ -161,8 +135,7 @@ export class PersistentMemory {
|
|
161
135
|
* Store a memory in the database
|
162
136
|
*/
|
163
137
|
async createMemory(memory: Memory) {
|
164
|
-
|
165
|
-
await this._getOrCreateIndex(indexName);
|
138
|
+
await this._getOrCreateIndex(memory.roomId);
|
166
139
|
|
167
140
|
const chunks = await this.processContent(memory.data);
|
168
141
|
|
@@ -173,7 +146,7 @@ export class PersistentMemory {
|
|
173
146
|
};
|
174
147
|
|
175
148
|
const response = await this._makeRequest(
|
176
|
-
`/indexes/${
|
149
|
+
`/indexes/${this.INDEX_PREFIX}/documents`,
|
177
150
|
{
|
178
151
|
method: "POST",
|
179
152
|
body: JSON.stringify([document]),
|
@@ -186,7 +159,7 @@ export class PersistentMemory {
|
|
186
159
|
/**
|
187
160
|
* Find best matching memories
|
188
161
|
*/
|
189
|
-
async
|
162
|
+
async findRelevantDocuments(query: string, options: SearchOptions = {}) {
|
190
163
|
console.log("\n🔍 Searching in persistent memory");
|
191
164
|
console.log("Query:", query);
|
192
165
|
console.log("Options:", JSON.stringify(options, null, 2));
|
@@ -199,42 +172,20 @@ export class PersistentMemory {
|
|
199
172
|
|
200
173
|
const searchResults = [];
|
201
174
|
|
202
|
-
|
203
|
-
|
204
|
-
const
|
205
|
-
|
206
|
-
try {
|
207
|
-
const globalResults = await this._makeRequest<MeilisearchResponse>(
|
208
|
-
`/indexes/${globalIndex}/search`,
|
209
|
-
{
|
210
|
-
method: "POST",
|
211
|
-
body: JSON.stringify({ q: query }),
|
212
|
-
}
|
213
|
-
);
|
214
|
-
if (globalResults?.hits) {
|
215
|
-
searchResults.push(...globalResults.hits);
|
216
|
-
}
|
217
|
-
} catch (error) {
|
218
|
-
console.error("❌ Error searching global index:", error);
|
219
|
-
}
|
220
|
-
}
|
221
|
-
|
222
|
-
// Search in user memories
|
223
|
-
if (
|
224
|
-
options.userId &&
|
225
|
-
(!options.scope || options.scope === MemoryScope.USER)
|
226
|
-
) {
|
227
|
-
const userIndex = this._getIndexName(MemoryScope.USER, options.userId);
|
228
|
-
const userResults = await this._makeRequest<MeilisearchResponse>(
|
229
|
-
`/indexes/${userIndex}/search`,
|
175
|
+
console.log("\n📚 Searching in global index:", this.INDEX_PREFIX);
|
176
|
+
try {
|
177
|
+
const globalResults = await this._makeRequest<MeilisearchResponse>(
|
178
|
+
`/indexes/${this.INDEX_PREFIX}/search`,
|
230
179
|
{
|
231
180
|
method: "POST",
|
232
181
|
body: JSON.stringify({ q: query }),
|
233
182
|
}
|
234
183
|
);
|
235
|
-
if (
|
236
|
-
searchResults.push(...
|
184
|
+
if (globalResults?.hits) {
|
185
|
+
searchResults.push(...globalResults.hits);
|
237
186
|
}
|
187
|
+
} catch (error) {
|
188
|
+
console.error("❌ Error searching global index:", error);
|
238
189
|
}
|
239
190
|
|
240
191
|
const totalResults = searchResults.length;
|
@@ -244,13 +195,11 @@ export class PersistentMemory {
|
|
244
195
|
const results = searchResults
|
245
196
|
.flatMap((hit) => {
|
246
197
|
const chunkSimilarities = hit.chunks.map((chunk) => ({
|
247
|
-
createdAt: hit.createdAt,
|
248
|
-
data: hit.data,
|
249
|
-
purpose: hit.purpose,
|
250
198
|
query: hit.query,
|
251
|
-
|
199
|
+
data: hit.data,
|
252
200
|
similarityPercentage:
|
253
201
|
(cosineSimilarity(queryEmbedding, chunk.embedding) + 1) * 50,
|
202
|
+
createdAt: hit.createdAt,
|
254
203
|
}));
|
255
204
|
|
256
205
|
return chunkSimilarities.reduce(
|
@@ -275,10 +224,6 @@ export class PersistentMemory {
|
|
275
224
|
results.forEach((match, index) => {
|
276
225
|
console.log(`\n${index + 1}. Match Details:`);
|
277
226
|
console.log(` Query: ${match.query}`);
|
278
|
-
console.log(` Purpose: ${match.purpose}`);
|
279
|
-
console.log(` Similarity: ${match.similarityPercentage.toFixed(2)}%`);
|
280
|
-
console.log(` Content: "${match.chunk}"`);
|
281
|
-
console.log("─".repeat(50));
|
282
227
|
});
|
283
228
|
} else {
|
284
229
|
console.log("\n❌ No relevant matches found");
|
@@ -290,9 +235,8 @@ export class PersistentMemory {
|
|
290
235
|
/**
|
291
236
|
* Delete memories for a given scope and user
|
292
237
|
*/
|
293
|
-
async deleteMemories(
|
294
|
-
|
295
|
-
return this._makeRequest(`/indexes/${indexName}`, {
|
238
|
+
async deleteMemories() {
|
239
|
+
return this._makeRequest(`/indexes/${this.INDEX_PREFIX}`, {
|
296
240
|
method: "DELETE",
|
297
241
|
});
|
298
242
|
}
|
package/package.json
CHANGED
@@ -0,0 +1,167 @@
|
|
1
|
+
import { Orchestrator } from "../llm/orchestrator";
|
2
|
+
import { ActionSchema, ScheduledAction, ScheduledActionEvents } from "../types";
|
3
|
+
import { ActionQueueManager } from "./queue";
|
4
|
+
|
5
|
+
export class ActionScheduler {
|
6
|
+
private scheduledActions: Map<string, NodeJS.Timeout> = new Map();
|
7
|
+
private storage: ScheduledActionStorage;
|
8
|
+
private events: ScheduledActionEvents;
|
9
|
+
|
10
|
+
constructor(
|
11
|
+
private actionQueueManager: ActionQueueManager,
|
12
|
+
private orchestrator: Orchestrator,
|
13
|
+
events: ScheduledActionEvents = {}
|
14
|
+
) {
|
15
|
+
this.storage = new ScheduledActionStorage();
|
16
|
+
this.events = events;
|
17
|
+
this.initializeScheduledActions();
|
18
|
+
}
|
19
|
+
|
20
|
+
async scheduleAction(
|
21
|
+
action: ActionSchema,
|
22
|
+
scheduledTime: Date,
|
23
|
+
userId: string,
|
24
|
+
recurrence?: ScheduledAction["recurrence"]
|
25
|
+
): Promise<string> {
|
26
|
+
const scheduledAction: ScheduledAction = {
|
27
|
+
id: crypto.randomUUID(),
|
28
|
+
action: {
|
29
|
+
name: action.name,
|
30
|
+
parameters: [],
|
31
|
+
},
|
32
|
+
scheduledTime,
|
33
|
+
userId,
|
34
|
+
status: "pending",
|
35
|
+
recurrence,
|
36
|
+
};
|
37
|
+
|
38
|
+
await this.storage.saveScheduledAction(scheduledAction);
|
39
|
+
this.scheduleExecution(scheduledAction);
|
40
|
+
this.events.onActionScheduled?.(scheduledAction);
|
41
|
+
|
42
|
+
return scheduledAction.id;
|
43
|
+
}
|
44
|
+
|
45
|
+
private async initializeScheduledActions() {
|
46
|
+
const pendingActions = await this.storage.getPendingActions();
|
47
|
+
pendingActions.forEach((action) => this.scheduleExecution(action));
|
48
|
+
}
|
49
|
+
|
50
|
+
private scheduleExecution(scheduledAction: ScheduledAction) {
|
51
|
+
const now = new Date();
|
52
|
+
const delay = scheduledAction.scheduledTime.getTime() - now.getTime();
|
53
|
+
|
54
|
+
if (delay < 0) return;
|
55
|
+
|
56
|
+
const timeout = setTimeout(async () => {
|
57
|
+
try {
|
58
|
+
await this.executeScheduledAction(scheduledAction);
|
59
|
+
|
60
|
+
if (scheduledAction.recurrence) {
|
61
|
+
const nextExecutionTime = this.calculateNextExecutionTime(
|
62
|
+
scheduledAction.scheduledTime,
|
63
|
+
scheduledAction.recurrence
|
64
|
+
);
|
65
|
+
const actionSchema = this.orchestrator.tools.find(
|
66
|
+
(tool: ActionSchema) => tool.name === scheduledAction.action.name
|
67
|
+
);
|
68
|
+
if (actionSchema) {
|
69
|
+
await this.scheduleAction(
|
70
|
+
actionSchema,
|
71
|
+
nextExecutionTime,
|
72
|
+
scheduledAction.userId,
|
73
|
+
scheduledAction.recurrence
|
74
|
+
);
|
75
|
+
}
|
76
|
+
}
|
77
|
+
} catch (error) {
|
78
|
+
console.error(
|
79
|
+
`Failed to execute scheduled action ${scheduledAction.id}:`,
|
80
|
+
error
|
81
|
+
);
|
82
|
+
await this.storage.updateActionStatus(scheduledAction.id, "failed");
|
83
|
+
}
|
84
|
+
}, delay);
|
85
|
+
|
86
|
+
this.scheduledActions.set(scheduledAction.id, timeout);
|
87
|
+
}
|
88
|
+
|
89
|
+
private async executeScheduledAction(scheduledAction: ScheduledAction) {
|
90
|
+
try {
|
91
|
+
this.events.onActionStart?.(scheduledAction);
|
92
|
+
|
93
|
+
this.actionQueueManager.addToQueue({
|
94
|
+
name: scheduledAction.action.name,
|
95
|
+
parameters: scheduledAction.action.parameters,
|
96
|
+
});
|
97
|
+
|
98
|
+
const result = await this.actionQueueManager.processQueue();
|
99
|
+
await this.storage.updateActionStatus(scheduledAction.id, "completed");
|
100
|
+
|
101
|
+
this.events.onActionComplete?.(scheduledAction, result);
|
102
|
+
} catch (error) {
|
103
|
+
await this.storage.updateActionStatus(scheduledAction.id, "failed");
|
104
|
+
this.events.onActionFailed?.(scheduledAction, error as Error);
|
105
|
+
throw error;
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
private calculateNextExecutionTime(
|
110
|
+
currentTime: Date,
|
111
|
+
recurrence: NonNullable<ScheduledAction["recurrence"]>
|
112
|
+
): Date {
|
113
|
+
const nextTime = new Date(currentTime);
|
114
|
+
|
115
|
+
switch (recurrence.type) {
|
116
|
+
case "daily":
|
117
|
+
nextTime.setDate(nextTime.getDate() + recurrence.interval);
|
118
|
+
break;
|
119
|
+
case "weekly":
|
120
|
+
nextTime.setDate(nextTime.getDate() + 7 * recurrence.interval);
|
121
|
+
break;
|
122
|
+
case "monthly":
|
123
|
+
nextTime.setMonth(nextTime.getMonth() + recurrence.interval);
|
124
|
+
break;
|
125
|
+
}
|
126
|
+
|
127
|
+
return nextTime;
|
128
|
+
}
|
129
|
+
|
130
|
+
async cancelScheduledAction(actionId: string): Promise<boolean> {
|
131
|
+
const timeout = this.scheduledActions.get(actionId);
|
132
|
+
if (timeout) {
|
133
|
+
clearTimeout(timeout);
|
134
|
+
this.scheduledActions.delete(actionId);
|
135
|
+
await this.storage.deleteScheduledAction(actionId);
|
136
|
+
this.events.onActionCancelled?.(actionId);
|
137
|
+
return true;
|
138
|
+
}
|
139
|
+
return false;
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
class ScheduledActionStorage {
|
144
|
+
private actions: ScheduledAction[] = [];
|
145
|
+
|
146
|
+
async saveScheduledAction(action: ScheduledAction): Promise<void> {
|
147
|
+
this.actions.push(action);
|
148
|
+
}
|
149
|
+
|
150
|
+
async getPendingActions(): Promise<ScheduledAction[]> {
|
151
|
+
return this.actions.filter((action) => action.status === "pending");
|
152
|
+
}
|
153
|
+
|
154
|
+
async updateActionStatus(
|
155
|
+
actionId: string,
|
156
|
+
status: ScheduledAction["status"]
|
157
|
+
): Promise<void> {
|
158
|
+
const action = this.actions.find((a) => a.id === actionId);
|
159
|
+
if (action) {
|
160
|
+
action.status = status;
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
async deleteScheduledAction(actionId: string): Promise<void> {
|
165
|
+
this.actions = this.actions.filter((a) => a.id !== actionId);
|
166
|
+
}
|
167
|
+
}
|