@ai.ntellect/core 0.3.0 → 0.3.3
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/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
|
+
}
|