@robota-sdk/agent-plugin 3.0.0-beta.64
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/LICENSE +21 -0
- package/dist/node/index.cjs +1 -0
- package/dist/node/index.d.ts +1724 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +2 -0
- package/dist/node/index.js.map +1 -0
- package/package.json +48 -0
- package/src/conversation-history/__tests__/conversation-history-plugin.test.ts +221 -0
- package/src/conversation-history/__tests__/history-storages.test.ts +115 -0
- package/src/conversation-history/conversation-history-helpers.ts +120 -0
- package/src/conversation-history/conversation-history-plugin.ts +294 -0
- package/src/conversation-history/index.ts +11 -0
- package/src/conversation-history/storages/database-storage.ts +96 -0
- package/src/conversation-history/storages/file-storage.ts +95 -0
- package/src/conversation-history/storages/index.ts +3 -0
- package/src/conversation-history/storages/memory-storage.ts +44 -0
- package/src/conversation-history/types.ts +64 -0
- package/src/error-handling/__tests__/error-handling-plugin.test.ts +201 -0
- package/src/error-handling/context-adapter.ts +48 -0
- package/src/error-handling/error-handling-helpers.ts +53 -0
- package/src/error-handling/error-handling-plugin.ts +293 -0
- package/src/error-handling/index.ts +9 -0
- package/src/error-handling/types.ts +82 -0
- package/src/execution-analytics/__tests__/execution-analytics-plugin.test.ts +224 -0
- package/src/execution-analytics/analytics-aggregation.ts +88 -0
- package/src/execution-analytics/execution-analytics-helpers.ts +83 -0
- package/src/execution-analytics/execution-analytics-plugin.ts +315 -0
- package/src/execution-analytics/index.ts +9 -0
- package/src/execution-analytics/types.ts +97 -0
- package/src/index.ts +8 -0
- package/src/limits/__tests__/limits-plugin.test.ts +712 -0
- package/src/limits/index.ts +9 -0
- package/src/limits/limits-helpers.ts +185 -0
- package/src/limits/limits-plugin.ts +196 -0
- package/src/limits/types.ts +73 -0
- package/src/limits/validation.ts +81 -0
- package/src/logging/__tests__/formatters.test.ts +48 -0
- package/src/logging/__tests__/logging-plugin.test.ts +464 -0
- package/src/logging/__tests__/logging-storages.test.ts +95 -0
- package/src/logging/formatters.ts +28 -0
- package/src/logging/index.ts +15 -0
- package/src/logging/logging-helpers.ts +223 -0
- package/src/logging/logging-plugin.ts +288 -0
- package/src/logging/storages/console-storage.ts +44 -0
- package/src/logging/storages/file-storage.ts +44 -0
- package/src/logging/storages/index.ts +4 -0
- package/src/logging/storages/remote-storage.ts +78 -0
- package/src/logging/storages/silent-storage.ts +18 -0
- package/src/logging/types.ts +106 -0
- package/src/performance/__tests__/memory-storage.test.ts +86 -0
- package/src/performance/__tests__/performance-plugin.test.ts +208 -0
- package/src/performance/__tests__/system-metrics-collector.test.ts +33 -0
- package/src/performance/collectors/system-metrics-collector.ts +69 -0
- package/src/performance/index.ts +12 -0
- package/src/performance/performance-helpers.ts +86 -0
- package/src/performance/performance-plugin.ts +274 -0
- package/src/performance/storages/index.ts +1 -0
- package/src/performance/storages/memory-storage.ts +88 -0
- package/src/performance/types.ts +160 -0
- package/src/usage/__tests__/aggregate-usage-stats.test.ts +136 -0
- package/src/usage/__tests__/memory-storage.test.ts +83 -0
- package/src/usage/__tests__/silent-storage.test.ts +44 -0
- package/src/usage/__tests__/usage-plugin-helpers.test.ts +155 -0
- package/src/usage/__tests__/usage-plugin.test.ts +358 -0
- package/src/usage/aggregate-usage-stats.ts +142 -0
- package/src/usage/index.ts +14 -0
- package/src/usage/storages/file-storage.ts +115 -0
- package/src/usage/storages/index.ts +4 -0
- package/src/usage/storages/memory-storage.ts +61 -0
- package/src/usage/storages/remote-storage.ts +143 -0
- package/src/usage/storages/silent-storage.ts +38 -0
- package/src/usage/types.ts +132 -0
- package/src/usage/usage-plugin-helpers.ts +116 -0
- package/src/usage/usage-plugin.ts +296 -0
- package/src/webhook/__tests__/webhook-plugin.test.ts +560 -0
- package/src/webhook/http-client.ts +141 -0
- package/src/webhook/index.ts +9 -0
- package/src/webhook/transformer.ts +209 -0
- package/src/webhook/types.ts +201 -0
- package/src/webhook/webhook-helpers.ts +60 -0
- package/src/webhook/webhook-plugin.ts +298 -0
- package/src/webhook/webhook-queue.ts +148 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AbstractPlugin,
|
|
3
|
+
PluginCategory,
|
|
4
|
+
PluginPriority,
|
|
5
|
+
type TUniversalMessage,
|
|
6
|
+
createLogger,
|
|
7
|
+
type ILogger,
|
|
8
|
+
PluginError,
|
|
9
|
+
type TTimerId,
|
|
10
|
+
startPeriodicTask,
|
|
11
|
+
stopPeriodicTask,
|
|
12
|
+
} from '@robota-sdk/agent-core';
|
|
13
|
+
import {
|
|
14
|
+
IConversationHistoryPluginOptions,
|
|
15
|
+
IConversationHistoryPluginStats,
|
|
16
|
+
IConversationHistoryEntry,
|
|
17
|
+
IHistoryStorage,
|
|
18
|
+
} from './types';
|
|
19
|
+
import {
|
|
20
|
+
validateConversationHistoryOptions,
|
|
21
|
+
createHistoryStorage,
|
|
22
|
+
loadConversationEntry,
|
|
23
|
+
savePendingConversations,
|
|
24
|
+
} from './conversation-history-helpers';
|
|
25
|
+
|
|
26
|
+
const DEFAULT_MAX_CONVERSATIONS = 100;
|
|
27
|
+
const DEFAULT_MAX_MESSAGES = 1000;
|
|
28
|
+
const DEFAULT_SAVE_INTERVAL_MS = 30000;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Persists conversation history using configurable storage backends.
|
|
32
|
+
*
|
|
33
|
+
* Supports memory, file, and database storage strategies. Messages are
|
|
34
|
+
* automatically trimmed to {@link IConversationHistoryPluginOptions.maxMessagesPerConversation | maxMessagesPerConversation}.
|
|
35
|
+
* When {@link IConversationHistoryPluginOptions.autoSave | autoSave} is
|
|
36
|
+
* disabled, changes are batched and flushed on a timer.
|
|
37
|
+
*
|
|
38
|
+
* @extends AbstractPlugin
|
|
39
|
+
* @see IHistoryStorage - storage backend contract
|
|
40
|
+
* @see IConversationHistoryPluginOptions - configuration options
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* const plugin = new ConversationHistoryPlugin({
|
|
45
|
+
* storage: 'memory',
|
|
46
|
+
* maxMessagesPerConversation: 500,
|
|
47
|
+
* });
|
|
48
|
+
* await plugin.startConversation('conv-1');
|
|
49
|
+
* await plugin.addMessage({ role: 'user', content: 'Hello' });
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export class ConversationHistoryPlugin extends AbstractPlugin<
|
|
53
|
+
IConversationHistoryPluginOptions,
|
|
54
|
+
IConversationHistoryPluginStats
|
|
55
|
+
> {
|
|
56
|
+
name = 'ConversationHistoryPlugin';
|
|
57
|
+
version = '1.0.0';
|
|
58
|
+
|
|
59
|
+
private storage: IHistoryStorage;
|
|
60
|
+
private pluginOptions: Required<IConversationHistoryPluginOptions>;
|
|
61
|
+
private logger: ILogger;
|
|
62
|
+
private currentConversationId?: string;
|
|
63
|
+
private batchSaveTimer?: TTimerId;
|
|
64
|
+
private pendingSaves = new Set<string>();
|
|
65
|
+
|
|
66
|
+
constructor(options: IConversationHistoryPluginOptions) {
|
|
67
|
+
super();
|
|
68
|
+
this.logger = createLogger('ConversationHistoryPlugin');
|
|
69
|
+
|
|
70
|
+
// Set plugin classification
|
|
71
|
+
this.category = PluginCategory.STORAGE;
|
|
72
|
+
this.priority = PluginPriority.HIGH;
|
|
73
|
+
|
|
74
|
+
// Validate options
|
|
75
|
+
validateConversationHistoryOptions(options);
|
|
76
|
+
|
|
77
|
+
// Set defaults
|
|
78
|
+
this.pluginOptions = {
|
|
79
|
+
enabled: options.enabled ?? true,
|
|
80
|
+
storage: options.storage,
|
|
81
|
+
maxConversations: options.maxConversations ?? DEFAULT_MAX_CONVERSATIONS,
|
|
82
|
+
maxMessagesPerConversation: options.maxMessagesPerConversation ?? DEFAULT_MAX_MESSAGES,
|
|
83
|
+
filePath: options.filePath ?? './conversations.json',
|
|
84
|
+
connectionString: options.connectionString ?? '',
|
|
85
|
+
autoSave: options.autoSave ?? true,
|
|
86
|
+
saveInterval: options.saveInterval ?? DEFAULT_SAVE_INTERVAL_MS,
|
|
87
|
+
// Add plugin options defaults
|
|
88
|
+
category: options.category ?? PluginCategory.STORAGE,
|
|
89
|
+
priority: options.priority ?? PluginPriority.HIGH,
|
|
90
|
+
moduleEvents: options.moduleEvents ?? [],
|
|
91
|
+
subscribeToAllModuleEvents: options.subscribeToAllModuleEvents ?? false,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Initialize storage
|
|
95
|
+
this.storage = createHistoryStorage(
|
|
96
|
+
this.pluginOptions.storage,
|
|
97
|
+
this.pluginOptions.maxConversations,
|
|
98
|
+
this.pluginOptions.filePath,
|
|
99
|
+
this.pluginOptions.connectionString,
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Setup batch saving if not auto-saving
|
|
103
|
+
if (!this.pluginOptions.autoSave) {
|
|
104
|
+
this.setupBatchSaving();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this.logger.info('ConversationHistoryPlugin initialized', {
|
|
108
|
+
storage: this.pluginOptions.storage,
|
|
109
|
+
maxConversations: this.pluginOptions.maxConversations,
|
|
110
|
+
autoSave: this.pluginOptions.autoSave,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Creates a new conversation entry and persists it (or queues for batch save).
|
|
116
|
+
* @throws PluginError if the storage write fails
|
|
117
|
+
*/
|
|
118
|
+
async startConversation(conversationId: string): Promise<void> {
|
|
119
|
+
try {
|
|
120
|
+
this.currentConversationId = conversationId;
|
|
121
|
+
|
|
122
|
+
const entry: IConversationHistoryEntry = {
|
|
123
|
+
conversationId,
|
|
124
|
+
messages: [],
|
|
125
|
+
startTime: new Date(),
|
|
126
|
+
lastUpdated: new Date(),
|
|
127
|
+
metadata: {},
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Always persist the initial entry so subsequent loads can find it.
|
|
131
|
+
// autoSave=false only means we track the id for deferred external flush via savePending.
|
|
132
|
+
await this.storage.save(conversationId, entry);
|
|
133
|
+
if (!this.pluginOptions.autoSave) {
|
|
134
|
+
this.pendingSaves.add(conversationId);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
this.logger.debug('Started new conversation', { conversationId });
|
|
138
|
+
} catch (error) {
|
|
139
|
+
throw new PluginError('Failed to start conversation', this.name, {
|
|
140
|
+
conversationId,
|
|
141
|
+
error: error instanceof Error ? error.message : String(error),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Appends a message to the active conversation, trimming to the maximum
|
|
148
|
+
* message limit if exceeded.
|
|
149
|
+
* @throws PluginError if no conversation is active or the storage write fails
|
|
150
|
+
*/
|
|
151
|
+
async addMessage(message: TUniversalMessage): Promise<void> {
|
|
152
|
+
if (!this.currentConversationId) {
|
|
153
|
+
throw new PluginError('No active conversation', this.name);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
const entry = (await this.storage.load(this.currentConversationId)) || {
|
|
158
|
+
conversationId: this.currentConversationId,
|
|
159
|
+
messages: [],
|
|
160
|
+
startTime: new Date(),
|
|
161
|
+
lastUpdated: new Date(),
|
|
162
|
+
metadata: {},
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// Add message and trim if necessary
|
|
166
|
+
entry.messages.push(message);
|
|
167
|
+
if (entry.messages.length > this.pluginOptions.maxMessagesPerConversation) {
|
|
168
|
+
entry.messages = entry.messages.slice(-this.pluginOptions.maxMessagesPerConversation);
|
|
169
|
+
}
|
|
170
|
+
entry.lastUpdated = new Date();
|
|
171
|
+
|
|
172
|
+
// Always persist the updated entry. autoSave=false tracks it as pending
|
|
173
|
+
// for deferred external flush via savePending.
|
|
174
|
+
await this.storage.save(this.currentConversationId, entry);
|
|
175
|
+
if (!this.pluginOptions.autoSave) {
|
|
176
|
+
this.pendingSaves.add(this.currentConversationId);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
this.logger.debug('Added message to conversation', {
|
|
180
|
+
conversationId: this.currentConversationId,
|
|
181
|
+
messageRole: message.role,
|
|
182
|
+
messageLength: message.content?.length ?? 0,
|
|
183
|
+
});
|
|
184
|
+
} catch (error) {
|
|
185
|
+
throw new PluginError('Failed to add message to conversation', this.name, {
|
|
186
|
+
conversationId: this.currentConversationId,
|
|
187
|
+
error: error instanceof Error ? error.message : String(error),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Load conversation history
|
|
194
|
+
*/
|
|
195
|
+
async loadConversation(conversationId: string): Promise<IConversationHistoryEntry | undefined> {
|
|
196
|
+
return loadConversationEntry(this.storage, conversationId, this.name, this.logger);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Get conversation history as messages
|
|
201
|
+
*/
|
|
202
|
+
async getHistory(conversationId: string): Promise<TUniversalMessage[]> {
|
|
203
|
+
const entry = await this.loadConversation(conversationId);
|
|
204
|
+
return entry?.messages ?? [];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* List all conversation IDs
|
|
209
|
+
*/
|
|
210
|
+
async listConversations(): Promise<string[]> {
|
|
211
|
+
try {
|
|
212
|
+
return await this.storage.list();
|
|
213
|
+
} catch (error) {
|
|
214
|
+
throw new PluginError('Failed to list conversations', this.name, {
|
|
215
|
+
error: error instanceof Error ? error.message : String(error),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Delete a conversation
|
|
222
|
+
*/
|
|
223
|
+
async deleteConversation(conversationId: string): Promise<boolean> {
|
|
224
|
+
try {
|
|
225
|
+
const deleted = await this.storage.delete(conversationId);
|
|
226
|
+
this.pendingSaves.delete(conversationId);
|
|
227
|
+
this.logger.debug('Deleted conversation', { conversationId, deleted });
|
|
228
|
+
return deleted;
|
|
229
|
+
} catch (error) {
|
|
230
|
+
throw new PluginError('Failed to delete conversation', this.name, {
|
|
231
|
+
conversationId,
|
|
232
|
+
error: error instanceof Error ? error.message : String(error),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Clear all conversations
|
|
239
|
+
*/
|
|
240
|
+
async clearAllConversations(): Promise<void> {
|
|
241
|
+
try {
|
|
242
|
+
await this.storage.clear();
|
|
243
|
+
this.pendingSaves.clear();
|
|
244
|
+
this.logger.info('Cleared all conversations');
|
|
245
|
+
} catch (error) {
|
|
246
|
+
throw new PluginError('Failed to clear conversations', this.name, {
|
|
247
|
+
error: error instanceof Error ? error.message : String(error),
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Persists all conversations queued since the last save (batch mode only).
|
|
254
|
+
* Individual save failures are logged but do not abort the remaining saves.
|
|
255
|
+
*/
|
|
256
|
+
async savePending(): Promise<void> {
|
|
257
|
+
await savePendingConversations(this.storage, this.pendingSaves, this.name, this.logger);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Stops the batch-save timer and flushes any pending conversation saves.
|
|
262
|
+
*/
|
|
263
|
+
async destroy(): Promise<void> {
|
|
264
|
+
try {
|
|
265
|
+
stopPeriodicTask(this.batchSaveTimer);
|
|
266
|
+
this.batchSaveTimer = undefined;
|
|
267
|
+
|
|
268
|
+
// Save any pending conversations
|
|
269
|
+
await this.savePending();
|
|
270
|
+
|
|
271
|
+
this.logger.info('ConversationHistoryPlugin destroyed');
|
|
272
|
+
} catch (error) {
|
|
273
|
+
this.logger.error('Error during plugin cleanup', {
|
|
274
|
+
error: error instanceof Error ? error.message : String(error),
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Setup batch saving timer
|
|
281
|
+
*/
|
|
282
|
+
private setupBatchSaving(): void {
|
|
283
|
+
this.batchSaveTimer = startPeriodicTask(
|
|
284
|
+
this.logger,
|
|
285
|
+
{
|
|
286
|
+
name: 'ConversationHistoryPlugin.savePending',
|
|
287
|
+
intervalMs: this.pluginOptions.saveInterval,
|
|
288
|
+
},
|
|
289
|
+
async () => {
|
|
290
|
+
await this.savePending();
|
|
291
|
+
},
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { ConversationHistoryPlugin } from './conversation-history-plugin';
|
|
2
|
+
export { MemoryHistoryStorage } from './storages/memory-storage';
|
|
3
|
+
export { FileHistoryStorage } from './storages/file-storage';
|
|
4
|
+
export { DatabaseHistoryStorage } from './storages/database-storage';
|
|
5
|
+
export type {
|
|
6
|
+
THistoryStorageStrategy,
|
|
7
|
+
IConversationHistoryEntry,
|
|
8
|
+
IConversationHistoryPluginOptions,
|
|
9
|
+
IConversationHistoryPluginStats,
|
|
10
|
+
IHistoryStorage,
|
|
11
|
+
} from './types';
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { IHistoryStorage, IConversationHistoryEntry } from '../types';
|
|
2
|
+
import { createLogger, type ILogger, StorageError } from '@robota-sdk/agent-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Database storage implementation
|
|
6
|
+
*/
|
|
7
|
+
export class DatabaseHistoryStorage implements IHistoryStorage {
|
|
8
|
+
private connectionString: string;
|
|
9
|
+
private logger: ILogger;
|
|
10
|
+
|
|
11
|
+
constructor(connectionString: string) {
|
|
12
|
+
this.connectionString = connectionString;
|
|
13
|
+
this.logger = createLogger('DatabaseHistoryStorage');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async save(conversationId: string, _entry: IConversationHistoryEntry): Promise<void> {
|
|
17
|
+
try {
|
|
18
|
+
// Database operations would be implemented here
|
|
19
|
+
this.logger.warn('Database storage not fully implemented yet', {
|
|
20
|
+
conversationId,
|
|
21
|
+
connectionString: this.maskConnectionString(),
|
|
22
|
+
});
|
|
23
|
+
} catch (error) {
|
|
24
|
+
throw new StorageError('Failed to save conversation to database', {
|
|
25
|
+
conversationId,
|
|
26
|
+
error: error instanceof Error ? error.message : String(error),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async load(conversationId: string): Promise<IConversationHistoryEntry | undefined> {
|
|
32
|
+
try {
|
|
33
|
+
// Database operations would be implemented here
|
|
34
|
+
this.logger.warn('Database storage not fully implemented yet', {
|
|
35
|
+
conversationId,
|
|
36
|
+
connectionString: this.maskConnectionString(),
|
|
37
|
+
});
|
|
38
|
+
return undefined;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
throw new StorageError('Failed to load conversation from database', {
|
|
41
|
+
conversationId,
|
|
42
|
+
error: error instanceof Error ? error.message : String(error),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async list(): Promise<string[]> {
|
|
48
|
+
try {
|
|
49
|
+
// Database operations would be implemented here
|
|
50
|
+
this.logger.warn('Database storage not fully implemented yet', {
|
|
51
|
+
connectionString: this.maskConnectionString(),
|
|
52
|
+
});
|
|
53
|
+
return [];
|
|
54
|
+
} catch (error) {
|
|
55
|
+
throw new StorageError('Failed to list conversations from database', {
|
|
56
|
+
error: error instanceof Error ? error.message : String(error),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async delete(conversationId: string): Promise<boolean> {
|
|
62
|
+
try {
|
|
63
|
+
// Database operations would be implemented here
|
|
64
|
+
this.logger.warn('Database storage not fully implemented yet', {
|
|
65
|
+
conversationId,
|
|
66
|
+
connectionString: this.maskConnectionString(),
|
|
67
|
+
});
|
|
68
|
+
return false;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
throw new StorageError('Failed to delete conversation from database', {
|
|
71
|
+
conversationId,
|
|
72
|
+
error: error instanceof Error ? error.message : String(error),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async clear(): Promise<void> {
|
|
78
|
+
try {
|
|
79
|
+
// Database operations would be implemented here
|
|
80
|
+
this.logger.warn('Database storage not fully implemented yet', {
|
|
81
|
+
connectionString: this.maskConnectionString(),
|
|
82
|
+
});
|
|
83
|
+
} catch (error) {
|
|
84
|
+
throw new StorageError('Failed to clear conversations from database', {
|
|
85
|
+
error: error instanceof Error ? error.message : String(error),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Mask sensitive information in connection string for logging
|
|
92
|
+
*/
|
|
93
|
+
private maskConnectionString(): string {
|
|
94
|
+
return this.connectionString.replace(/(:\/\/[^:]+:)[^@]+(@)/, '$1***$2');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { IHistoryStorage, IConversationHistoryEntry } from '../types';
|
|
2
|
+
import { createLogger, type ILogger, StorageError } from '@robota-sdk/agent-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* File storage implementation
|
|
6
|
+
*/
|
|
7
|
+
export class FileHistoryStorage implements IHistoryStorage {
|
|
8
|
+
private filePath: string;
|
|
9
|
+
private logger: ILogger;
|
|
10
|
+
|
|
11
|
+
constructor(filePath: string) {
|
|
12
|
+
this.filePath = filePath;
|
|
13
|
+
this.logger = createLogger('FileHistoryStorage');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async save(conversationId: string, _entry: IConversationHistoryEntry): Promise<void> {
|
|
17
|
+
try {
|
|
18
|
+
// File operations would be implemented here
|
|
19
|
+
// This is a placeholder for actual file system operations
|
|
20
|
+
this.logger.warn('File storage not fully implemented yet', {
|
|
21
|
+
conversationId,
|
|
22
|
+
filePath: this.filePath,
|
|
23
|
+
});
|
|
24
|
+
} catch (error) {
|
|
25
|
+
throw new StorageError('Failed to save conversation to file', {
|
|
26
|
+
conversationId,
|
|
27
|
+
filePath: this.filePath,
|
|
28
|
+
error: error instanceof Error ? error.message : String(error),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async load(conversationId: string): Promise<IConversationHistoryEntry | undefined> {
|
|
34
|
+
try {
|
|
35
|
+
// File operations would be implemented here
|
|
36
|
+
this.logger.warn('File storage not fully implemented yet', {
|
|
37
|
+
conversationId,
|
|
38
|
+
filePath: this.filePath,
|
|
39
|
+
});
|
|
40
|
+
return undefined;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
throw new StorageError('Failed to load conversation from file', {
|
|
43
|
+
conversationId,
|
|
44
|
+
filePath: this.filePath,
|
|
45
|
+
error: error instanceof Error ? error.message : String(error),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async list(): Promise<string[]> {
|
|
51
|
+
try {
|
|
52
|
+
// File operations would be implemented here
|
|
53
|
+
this.logger.warn('File storage not fully implemented yet', {
|
|
54
|
+
filePath: this.filePath,
|
|
55
|
+
});
|
|
56
|
+
return [];
|
|
57
|
+
} catch (error) {
|
|
58
|
+
throw new StorageError('Failed to list conversations from file', {
|
|
59
|
+
filePath: this.filePath,
|
|
60
|
+
error: error instanceof Error ? error.message : String(error),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async delete(conversationId: string): Promise<boolean> {
|
|
66
|
+
try {
|
|
67
|
+
// File operations would be implemented here
|
|
68
|
+
this.logger.warn('File storage not fully implemented yet', {
|
|
69
|
+
conversationId,
|
|
70
|
+
filePath: this.filePath,
|
|
71
|
+
});
|
|
72
|
+
return false;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
throw new StorageError('Failed to delete conversation from file', {
|
|
75
|
+
conversationId,
|
|
76
|
+
filePath: this.filePath,
|
|
77
|
+
error: error instanceof Error ? error.message : String(error),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async clear(): Promise<void> {
|
|
83
|
+
try {
|
|
84
|
+
// File operations would be implemented here
|
|
85
|
+
this.logger.warn('File storage not fully implemented yet', {
|
|
86
|
+
filePath: this.filePath,
|
|
87
|
+
});
|
|
88
|
+
} catch (error) {
|
|
89
|
+
throw new StorageError('Failed to clear conversations from file', {
|
|
90
|
+
filePath: this.filePath,
|
|
91
|
+
error: error instanceof Error ? error.message : String(error),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { IHistoryStorage, IConversationHistoryEntry } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Memory storage implementation
|
|
5
|
+
*/
|
|
6
|
+
export class MemoryHistoryStorage implements IHistoryStorage {
|
|
7
|
+
private conversations = new Map<string, IConversationHistoryEntry>();
|
|
8
|
+
private maxConversations: number;
|
|
9
|
+
|
|
10
|
+
constructor(maxConversations: number = 100) {
|
|
11
|
+
this.maxConversations = maxConversations;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async save(conversationId: string, entry: IConversationHistoryEntry): Promise<void> {
|
|
15
|
+
// Remove oldest conversation if limit exceeded
|
|
16
|
+
if (
|
|
17
|
+
this.conversations.size >= this.maxConversations &&
|
|
18
|
+
!this.conversations.has(conversationId)
|
|
19
|
+
) {
|
|
20
|
+
const oldestId = this.conversations.keys().next().value;
|
|
21
|
+
if (oldestId) {
|
|
22
|
+
this.conversations.delete(oldestId);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
this.conversations.set(conversationId, { ...entry });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async load(conversationId: string): Promise<IConversationHistoryEntry | undefined> {
|
|
30
|
+
return this.conversations.get(conversationId);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async list(): Promise<string[]> {
|
|
34
|
+
return Array.from(this.conversations.keys());
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async delete(conversationId: string): Promise<boolean> {
|
|
38
|
+
return this.conversations.delete(conversationId);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async clear(): Promise<void> {
|
|
42
|
+
this.conversations.clear();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { TUniversalMessage, IPluginOptions, IPluginStats } from '@robota-sdk/agent-core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Storage strategy for conversation history
|
|
5
|
+
*/
|
|
6
|
+
export type THistoryStorageStrategy = 'memory' | 'file' | 'database';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Configuration options for conversation history plugin
|
|
10
|
+
*/
|
|
11
|
+
export interface IConversationHistoryPluginOptions extends IPluginOptions {
|
|
12
|
+
/** Storage strategy to use */
|
|
13
|
+
storage: THistoryStorageStrategy;
|
|
14
|
+
/** Maximum number of conversations to store */
|
|
15
|
+
maxConversations?: number;
|
|
16
|
+
/** Maximum messages per conversation */
|
|
17
|
+
maxMessagesPerConversation?: number;
|
|
18
|
+
/** File path for file storage strategy */
|
|
19
|
+
filePath?: string;
|
|
20
|
+
/** Database connection string for database storage */
|
|
21
|
+
connectionString?: string;
|
|
22
|
+
/** Whether to auto-save after each message */
|
|
23
|
+
autoSave?: boolean;
|
|
24
|
+
/** Save interval in milliseconds for batch saving */
|
|
25
|
+
saveInterval?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Conversation history entry
|
|
30
|
+
*/
|
|
31
|
+
export interface IConversationHistoryEntry {
|
|
32
|
+
conversationId: string;
|
|
33
|
+
messages: TUniversalMessage[];
|
|
34
|
+
startTime: Date;
|
|
35
|
+
lastUpdated: Date;
|
|
36
|
+
metadata?: Record<string, string | number | boolean | Date>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Storage interface for conversation history
|
|
41
|
+
*/
|
|
42
|
+
export interface IHistoryStorage {
|
|
43
|
+
save(conversationId: string, entry: IConversationHistoryEntry): Promise<void>;
|
|
44
|
+
load(conversationId: string): Promise<IConversationHistoryEntry | undefined>;
|
|
45
|
+
list(): Promise<string[]>;
|
|
46
|
+
delete(conversationId: string): Promise<boolean>;
|
|
47
|
+
clear(): Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Conversation history plugin statistics
|
|
52
|
+
*/
|
|
53
|
+
export interface IConversationHistoryPluginStats extends IPluginStats {
|
|
54
|
+
/** Total number of conversations stored */
|
|
55
|
+
totalConversations: number;
|
|
56
|
+
/** Total number of messages stored */
|
|
57
|
+
totalMessages: number;
|
|
58
|
+
/** Storage strategy in use */
|
|
59
|
+
storageStrategy: THistoryStorageStrategy;
|
|
60
|
+
/** Last save timestamp */
|
|
61
|
+
lastSaveTime?: Date;
|
|
62
|
+
/** Number of failed saves */
|
|
63
|
+
failedSaves: number;
|
|
64
|
+
}
|