@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,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging Plugin - Validation and storage factory helpers.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from logging-plugin.ts to keep each file under 300 lines.
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { ConfigurationError, EVENT_EMITTER_EVENTS } from '@robota-sdk/agent-core';
|
|
9
|
+
import {
|
|
10
|
+
ConsoleLogStorage,
|
|
11
|
+
FileLogStorage,
|
|
12
|
+
RemoteLogStorage,
|
|
13
|
+
SilentLogStorage,
|
|
14
|
+
} from './storages/index';
|
|
15
|
+
import type { ILoggingPluginOptions, ILogStorage, TLogLevel, ILogEntry } from './types';
|
|
16
|
+
|
|
17
|
+
/** Validate LoggingPlugin constructor options. @internal */
|
|
18
|
+
export function validateLoggingOptions(options: ILoggingPluginOptions): void {
|
|
19
|
+
if (!options.strategy) {
|
|
20
|
+
throw new ConfigurationError('Logging strategy is required');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!['console', 'file', 'remote', 'silent'].includes(options.strategy)) {
|
|
24
|
+
throw new ConfigurationError('Invalid logging strategy', {
|
|
25
|
+
validStrategies: ['console', 'file', 'remote', 'silent'],
|
|
26
|
+
provided: options.strategy,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (options.level && !['debug', 'info', 'warn', 'error'].includes(options.level)) {
|
|
31
|
+
throw new ConfigurationError('Invalid log level', {
|
|
32
|
+
validLevels: ['debug', 'info', 'warn', 'error'],
|
|
33
|
+
provided: options.level,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (options.strategy === 'file' && !options.filePath) {
|
|
38
|
+
throw new ConfigurationError('File path is required for file logging strategy');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (options.strategy === 'remote' && !options.remoteEndpoint) {
|
|
42
|
+
throw new ConfigurationError('Remote endpoint is required for remote logging strategy');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (options.maxLogs !== undefined && options.maxLogs <= 0) {
|
|
46
|
+
throw new ConfigurationError('Max logs must be positive');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (options.batchSize !== undefined && options.batchSize <= 0) {
|
|
50
|
+
throw new ConfigurationError('Batch size must be positive');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (options.flushInterval !== undefined && options.flushInterval <= 0) {
|
|
54
|
+
throw new ConfigurationError('Flush interval must be positive');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Create ILogStorage instance for the given strategy. @internal */
|
|
59
|
+
export function createLoggingStorage(
|
|
60
|
+
strategy: string,
|
|
61
|
+
filePath: string,
|
|
62
|
+
remoteEndpoint: string,
|
|
63
|
+
flushInterval: number,
|
|
64
|
+
formatter?: ILoggingPluginOptions['formatter'],
|
|
65
|
+
): ILogStorage {
|
|
66
|
+
switch (strategy) {
|
|
67
|
+
case 'console':
|
|
68
|
+
return new ConsoleLogStorage(formatter);
|
|
69
|
+
case 'file':
|
|
70
|
+
return new FileLogStorage(filePath, formatter);
|
|
71
|
+
case 'remote':
|
|
72
|
+
return new RemoteLogStorage(remoteEndpoint, { timeout: flushInterval });
|
|
73
|
+
case 'silent':
|
|
74
|
+
return new SilentLogStorage();
|
|
75
|
+
default:
|
|
76
|
+
throw new ConfigurationError('Unknown logging strategy', { strategy });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Event name → log descriptor mapping for module lifecycle events. @internal */
|
|
81
|
+
export const LOGGING_MODULE_EVENT_MAP: ReadonlyMap<
|
|
82
|
+
string,
|
|
83
|
+
{ level: TLogLevel; message: string; operation: string }
|
|
84
|
+
> = new Map([
|
|
85
|
+
[
|
|
86
|
+
EVENT_EMITTER_EVENTS.MODULE_INITIALIZE_START,
|
|
87
|
+
{
|
|
88
|
+
level: 'info',
|
|
89
|
+
message: 'Module initialization started',
|
|
90
|
+
operation: 'module_initialize_start',
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
[
|
|
94
|
+
EVENT_EMITTER_EVENTS.MODULE_INITIALIZE_COMPLETE,
|
|
95
|
+
{
|
|
96
|
+
level: 'info',
|
|
97
|
+
message: 'Module initialization completed',
|
|
98
|
+
operation: 'module_initialize_complete',
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
EVENT_EMITTER_EVENTS.MODULE_INITIALIZE_ERROR,
|
|
103
|
+
{
|
|
104
|
+
level: 'error',
|
|
105
|
+
message: 'Module initialization failed',
|
|
106
|
+
operation: 'module_initialize_error',
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
[
|
|
110
|
+
EVENT_EMITTER_EVENTS.MODULE_EXECUTION_START,
|
|
111
|
+
{ level: 'debug', message: 'Module execution started', operation: 'module_execution_start' },
|
|
112
|
+
],
|
|
113
|
+
[
|
|
114
|
+
EVENT_EMITTER_EVENTS.MODULE_EXECUTION_COMPLETE,
|
|
115
|
+
{
|
|
116
|
+
level: 'debug',
|
|
117
|
+
message: 'Module execution completed',
|
|
118
|
+
operation: 'module_execution_complete',
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
[
|
|
122
|
+
EVENT_EMITTER_EVENTS.MODULE_EXECUTION_ERROR,
|
|
123
|
+
{ level: 'error', message: 'Module execution failed', operation: 'module_execution_error' },
|
|
124
|
+
],
|
|
125
|
+
[
|
|
126
|
+
EVENT_EMITTER_EVENTS.MODULE_DISPOSE_START,
|
|
127
|
+
{ level: 'debug', message: 'Module disposal started', operation: 'module_dispose_start' },
|
|
128
|
+
],
|
|
129
|
+
[
|
|
130
|
+
EVENT_EMITTER_EVENTS.MODULE_DISPOSE_COMPLETE,
|
|
131
|
+
{ level: 'info', message: 'Module disposal completed', operation: 'module_dispose_complete' },
|
|
132
|
+
],
|
|
133
|
+
[
|
|
134
|
+
EVENT_EMITTER_EVENTS.MODULE_DISPOSE_ERROR,
|
|
135
|
+
{ level: 'error', message: 'Module disposal failed', operation: 'module_dispose_error' },
|
|
136
|
+
],
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
/** Safely extract module data fields from untyped event payload. @internal */
|
|
140
|
+
export function extractLoggingModuleData(data: unknown): {
|
|
141
|
+
moduleName: string;
|
|
142
|
+
moduleType: string;
|
|
143
|
+
duration?: number;
|
|
144
|
+
success?: boolean;
|
|
145
|
+
} {
|
|
146
|
+
const record = typeof data === 'object' && data !== null ? (data as Record<string, unknown>) : {};
|
|
147
|
+
return {
|
|
148
|
+
moduleName: typeof record['moduleName'] === 'string' ? record['moduleName'] : 'unknown',
|
|
149
|
+
moduleType: typeof record['moduleType'] === 'string' ? record['moduleType'] : 'unknown',
|
|
150
|
+
...(typeof record['duration'] === 'number' && { duration: record['duration'] }),
|
|
151
|
+
...(typeof record['success'] === 'boolean' && { success: record['success'] }),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Narrow interface for the log methods needed by convenience helpers. @internal */
|
|
156
|
+
export interface ILogWriter {
|
|
157
|
+
info(
|
|
158
|
+
message: string,
|
|
159
|
+
context?: Record<string, string | number | boolean | Date | undefined>,
|
|
160
|
+
metadata?: ILogEntry['metadata'],
|
|
161
|
+
): Promise<void>;
|
|
162
|
+
log(
|
|
163
|
+
level: TLogLevel,
|
|
164
|
+
message: string,
|
|
165
|
+
context?: Record<string, string | number | boolean | Date | undefined>,
|
|
166
|
+
metadata?: ILogEntry['metadata'],
|
|
167
|
+
): Promise<void>;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const PREVIEW_LENGTH_HELPER = 100;
|
|
171
|
+
|
|
172
|
+
/** Log execution start with truncated user input. @internal */
|
|
173
|
+
export async function logExecutionStartHelper(
|
|
174
|
+
writer: ILogWriter,
|
|
175
|
+
executionId: string,
|
|
176
|
+
userInput: string,
|
|
177
|
+
metadata?: ILogEntry['metadata'],
|
|
178
|
+
): Promise<void> {
|
|
179
|
+
await writer.info(
|
|
180
|
+
'Execution started',
|
|
181
|
+
{ userInput: userInput.substring(0, PREVIEW_LENGTH_HELPER) },
|
|
182
|
+
{ executionId, operation: 'execution_start', ...metadata },
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Log execution completion with duration. @internal */
|
|
187
|
+
export async function logExecutionCompleteHelper(
|
|
188
|
+
writer: ILogWriter,
|
|
189
|
+
executionId: string,
|
|
190
|
+
duration: number,
|
|
191
|
+
metadata?: ILogEntry['metadata'],
|
|
192
|
+
): Promise<void> {
|
|
193
|
+
await writer.info(
|
|
194
|
+
'Execution completed',
|
|
195
|
+
{ duration },
|
|
196
|
+
{
|
|
197
|
+
executionId,
|
|
198
|
+
operation: 'execution_complete',
|
|
199
|
+
...(duration !== undefined && { duration }),
|
|
200
|
+
...metadata,
|
|
201
|
+
},
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Log a tool execution result at info (success) or error (failure) level. @internal */
|
|
206
|
+
export async function logToolExecutionHelper(
|
|
207
|
+
writer: ILogWriter,
|
|
208
|
+
toolName: string,
|
|
209
|
+
executionId: string,
|
|
210
|
+
duration?: number,
|
|
211
|
+
success?: boolean,
|
|
212
|
+
metadata?: ILogEntry['metadata'],
|
|
213
|
+
): Promise<void> {
|
|
214
|
+
const message = success ? 'Tool executed successfully' : 'Tool execution failed';
|
|
215
|
+
const level: TLogLevel = success ? 'info' : 'error';
|
|
216
|
+
const logMetadata = {
|
|
217
|
+
executionId,
|
|
218
|
+
operation: 'tool_execution',
|
|
219
|
+
...(duration !== undefined && { duration }),
|
|
220
|
+
...(metadata && typeof metadata === 'object' ? metadata : {}),
|
|
221
|
+
} as ILogEntry['metadata'];
|
|
222
|
+
await writer.log(level, message, { toolName, success: success ?? false }, logMetadata);
|
|
223
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AbstractPlugin,
|
|
3
|
+
PluginCategory,
|
|
4
|
+
PluginPriority,
|
|
5
|
+
createLogger,
|
|
6
|
+
type ILogger,
|
|
7
|
+
SilentLogger,
|
|
8
|
+
PluginError,
|
|
9
|
+
type IEventEmitterEventData,
|
|
10
|
+
type TEventName,
|
|
11
|
+
} from '@robota-sdk/agent-core';
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
TLogLevel,
|
|
15
|
+
ILogEntry,
|
|
16
|
+
ILoggingContextData,
|
|
17
|
+
ILoggingPluginOptions,
|
|
18
|
+
ILoggingPluginStats,
|
|
19
|
+
ILogStorage,
|
|
20
|
+
ILogFormatter,
|
|
21
|
+
} from './types';
|
|
22
|
+
import {
|
|
23
|
+
validateLoggingOptions,
|
|
24
|
+
createLoggingStorage,
|
|
25
|
+
extractLoggingModuleData,
|
|
26
|
+
LOGGING_MODULE_EVENT_MAP,
|
|
27
|
+
logExecutionStartHelper,
|
|
28
|
+
logExecutionCompleteHelper,
|
|
29
|
+
logToolExecutionHelper,
|
|
30
|
+
} from './logging-helpers';
|
|
31
|
+
|
|
32
|
+
export type { ILoggingContextData } from './types';
|
|
33
|
+
|
|
34
|
+
const DEFAULT_MAX_LOGS = 10000;
|
|
35
|
+
const DEFAULT_BATCH_SIZE = 100;
|
|
36
|
+
const DEFAULT_FLUSH_INTERVAL_MS = 30000;
|
|
37
|
+
|
|
38
|
+
/** Logs agent operations using configurable storage backends (console/file/remote/silent). */
|
|
39
|
+
export class LoggingPlugin extends AbstractPlugin<ILoggingPluginOptions, ILoggingPluginStats> {
|
|
40
|
+
name = 'LoggingPlugin';
|
|
41
|
+
version = '1.0.0';
|
|
42
|
+
|
|
43
|
+
private storage: ILogStorage;
|
|
44
|
+
private pluginOptions: Required<Omit<ILoggingPluginOptions, 'formatter' | 'logger'>> & {
|
|
45
|
+
formatter?: ILogFormatter;
|
|
46
|
+
logger?: ILogger;
|
|
47
|
+
};
|
|
48
|
+
private logger: ILogger;
|
|
49
|
+
private simpleLogger: ILogger;
|
|
50
|
+
private logLevels: TLogLevel[] = ['debug', 'info', 'warn', 'error'];
|
|
51
|
+
|
|
52
|
+
constructor(options: ILoggingPluginOptions) {
|
|
53
|
+
super();
|
|
54
|
+
this.logger = createLogger('LoggingPlugin');
|
|
55
|
+
this.simpleLogger = options.logger || SilentLogger;
|
|
56
|
+
|
|
57
|
+
// Set plugin classification
|
|
58
|
+
this.category = PluginCategory.LOGGING;
|
|
59
|
+
this.priority = PluginPriority.HIGH;
|
|
60
|
+
|
|
61
|
+
// Validate options
|
|
62
|
+
validateLoggingOptions(options);
|
|
63
|
+
|
|
64
|
+
// Set defaults
|
|
65
|
+
this.pluginOptions = {
|
|
66
|
+
enabled: options.enabled ?? true,
|
|
67
|
+
strategy: options.strategy,
|
|
68
|
+
level: options.level ?? 'info',
|
|
69
|
+
filePath: options.filePath ?? './agent.log',
|
|
70
|
+
remoteEndpoint: options.remoteEndpoint ?? '',
|
|
71
|
+
remoteHeaders: options.remoteHeaders ?? {},
|
|
72
|
+
maxLogs: options.maxLogs ?? DEFAULT_MAX_LOGS,
|
|
73
|
+
includeStackTrace: options.includeStackTrace ?? true,
|
|
74
|
+
...(options.formatter && { formatter: options.formatter }),
|
|
75
|
+
batchSize: options.batchSize ?? DEFAULT_BATCH_SIZE,
|
|
76
|
+
flushInterval: options.flushInterval ?? DEFAULT_FLUSH_INTERVAL_MS,
|
|
77
|
+
// Add plugin options defaults
|
|
78
|
+
category: options.category ?? PluginCategory.LOGGING,
|
|
79
|
+
priority: options.priority ?? PluginPriority.HIGH,
|
|
80
|
+
moduleEvents: options.moduleEvents ?? [],
|
|
81
|
+
subscribeToAllModuleEvents: options.subscribeToAllModuleEvents ?? false,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Initialize storage
|
|
85
|
+
this.storage = createLoggingStorage(
|
|
86
|
+
this.pluginOptions.strategy,
|
|
87
|
+
this.pluginOptions.filePath,
|
|
88
|
+
this.pluginOptions.remoteEndpoint,
|
|
89
|
+
this.pluginOptions.flushInterval,
|
|
90
|
+
this.pluginOptions.formatter,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
this.logger.info('LoggingPlugin initialized', {
|
|
94
|
+
strategy: this.pluginOptions.strategy,
|
|
95
|
+
level: this.pluginOptions.level,
|
|
96
|
+
maxLogs: this.pluginOptions.maxLogs,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Routes module lifecycle events (initialize, execute, dispose) to the
|
|
102
|
+
* appropriate log level. Errors are logged but never re-thrown.
|
|
103
|
+
*/
|
|
104
|
+
override async onModuleEvent(
|
|
105
|
+
eventName: TEventName,
|
|
106
|
+
eventData: IEventEmitterEventData,
|
|
107
|
+
): Promise<void> {
|
|
108
|
+
try {
|
|
109
|
+
const descriptor = LOGGING_MODULE_EVENT_MAP.get(eventName);
|
|
110
|
+
if (!descriptor) return;
|
|
111
|
+
|
|
112
|
+
const { moduleName, moduleType, duration, success } = extractLoggingModuleData(
|
|
113
|
+
eventData.data,
|
|
114
|
+
);
|
|
115
|
+
const context: ILoggingContextData = { moduleName, moduleType };
|
|
116
|
+
if (duration !== undefined) context.duration = duration;
|
|
117
|
+
if (success !== undefined) context.success = success;
|
|
118
|
+
|
|
119
|
+
const metadata: ILogEntry['metadata'] = { operation: descriptor.operation };
|
|
120
|
+
if (eventData.executionId) metadata.executionId = eventData.executionId;
|
|
121
|
+
if (duration !== undefined) metadata.duration = duration;
|
|
122
|
+
|
|
123
|
+
const isErrorEvent = descriptor.level === 'error';
|
|
124
|
+
if (isErrorEvent) {
|
|
125
|
+
await this.error(descriptor.message, eventData.error, context, metadata);
|
|
126
|
+
} else {
|
|
127
|
+
await this.log(descriptor.level, descriptor.message, context, metadata);
|
|
128
|
+
}
|
|
129
|
+
} catch (error) {
|
|
130
|
+
this.simpleLogger.error(
|
|
131
|
+
`LoggingPlugin failed to handle module event ${eventName}:`,
|
|
132
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Writes a log entry to the configured storage if the level meets the
|
|
139
|
+
* current threshold. Logging failures are swallowed to prevent cascading errors.
|
|
140
|
+
*/
|
|
141
|
+
async log(
|
|
142
|
+
level: TLogLevel,
|
|
143
|
+
message: string,
|
|
144
|
+
context?: ILoggingContextData,
|
|
145
|
+
metadata?: ILogEntry['metadata'],
|
|
146
|
+
): Promise<void> {
|
|
147
|
+
if (!this.shouldLog(level)) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
const entry: ILogEntry = {
|
|
153
|
+
timestamp: new Date(),
|
|
154
|
+
level,
|
|
155
|
+
message,
|
|
156
|
+
...(context && { context }),
|
|
157
|
+
...(metadata && { metadata }),
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
await this.storage.write(entry);
|
|
161
|
+
} catch (error) {
|
|
162
|
+
// Don't throw errors from logging to avoid infinite loops
|
|
163
|
+
this.simpleLogger.error(
|
|
164
|
+
'Logging failed:',
|
|
165
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Log debug message */
|
|
171
|
+
async debug(
|
|
172
|
+
message: string,
|
|
173
|
+
context?: ILoggingContextData,
|
|
174
|
+
metadata?: ILogEntry['metadata'],
|
|
175
|
+
): Promise<void> {
|
|
176
|
+
await this.log('debug', message, context, metadata);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Log info message */
|
|
180
|
+
async info(
|
|
181
|
+
message: string,
|
|
182
|
+
context?: ILoggingContextData,
|
|
183
|
+
metadata?: ILogEntry['metadata'],
|
|
184
|
+
): Promise<void> {
|
|
185
|
+
await this.log('info', message, context, metadata);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Log warning message */
|
|
189
|
+
async warn(
|
|
190
|
+
message: string,
|
|
191
|
+
context?: ILoggingContextData,
|
|
192
|
+
metadata?: ILogEntry['metadata'],
|
|
193
|
+
): Promise<void> {
|
|
194
|
+
await this.log('warn', message, context, metadata);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Log error message
|
|
199
|
+
*/
|
|
200
|
+
async error(
|
|
201
|
+
message: string,
|
|
202
|
+
error?: Error,
|
|
203
|
+
context?: ILoggingContextData,
|
|
204
|
+
metadata?: ILogEntry['metadata'],
|
|
205
|
+
): Promise<void> {
|
|
206
|
+
const errorContext = {
|
|
207
|
+
...context,
|
|
208
|
+
...(error && this.pluginOptions.includeStackTrace
|
|
209
|
+
? {
|
|
210
|
+
errorMessage: error.message,
|
|
211
|
+
errorStack: error.stack,
|
|
212
|
+
}
|
|
213
|
+
: {}),
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
await this.log('error', message, errorContext, metadata);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Logs the start of an agent execution, truncating user input to 100 characters.
|
|
221
|
+
*/
|
|
222
|
+
async logExecutionStart(
|
|
223
|
+
executionId: string,
|
|
224
|
+
userInput: string,
|
|
225
|
+
metadata?: ILogEntry['metadata'],
|
|
226
|
+
): Promise<void> {
|
|
227
|
+
await logExecutionStartHelper(this, executionId, userInput, metadata);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Logs the completion of an agent execution with its duration.
|
|
232
|
+
*/
|
|
233
|
+
async logExecutionComplete(
|
|
234
|
+
executionId: string,
|
|
235
|
+
duration: number,
|
|
236
|
+
metadata?: ILogEntry['metadata'],
|
|
237
|
+
): Promise<void> {
|
|
238
|
+
await logExecutionCompleteHelper(this, executionId, duration, metadata);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Logs a tool execution result at info (success) or error (failure) level.
|
|
243
|
+
*/
|
|
244
|
+
async logToolExecution(
|
|
245
|
+
toolName: string,
|
|
246
|
+
executionId: string,
|
|
247
|
+
duration?: number,
|
|
248
|
+
success?: boolean,
|
|
249
|
+
metadata?: ILogEntry['metadata'],
|
|
250
|
+
): Promise<void> {
|
|
251
|
+
await logToolExecutionHelper(this, toolName, executionId, duration, success, metadata);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Flushes any buffered log entries to the underlying storage.
|
|
256
|
+
* @throws PluginError if the flush operation fails
|
|
257
|
+
*/
|
|
258
|
+
async flush(): Promise<void> {
|
|
259
|
+
try {
|
|
260
|
+
await this.storage.flush();
|
|
261
|
+
} catch (error) {
|
|
262
|
+
throw new PluginError('Failed to flush logs', this.name, {
|
|
263
|
+
error: error instanceof Error ? error.message : String(error),
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Closes the underlying storage and releases resources.
|
|
270
|
+
*/
|
|
271
|
+
async destroy(): Promise<void> {
|
|
272
|
+
try {
|
|
273
|
+
await this.storage.close();
|
|
274
|
+
this.logger.info('LoggingPlugin destroyed');
|
|
275
|
+
} catch (error) {
|
|
276
|
+
this.logger.error('Error during plugin cleanup', {
|
|
277
|
+
error: error instanceof Error ? error.message : String(error),
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** Check if message should be logged based on level */
|
|
283
|
+
private shouldLog(level: TLogLevel): boolean {
|
|
284
|
+
const currentLevelIndex = this.logLevels.indexOf(this.pluginOptions.level);
|
|
285
|
+
const messageLevelIndex = this.logLevels.indexOf(level);
|
|
286
|
+
return messageLevelIndex >= currentLevelIndex;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ILogEntry, ILogStorage, ILogFormatter, TLogLevel } from '../types';
|
|
2
|
+
import { ConsoleLogFormatter } from '../formatters';
|
|
3
|
+
import { SilentLogger, type ILogger } from '@robota-sdk/agent-core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Console log storage
|
|
7
|
+
*/
|
|
8
|
+
export class ConsoleLogStorage implements ILogStorage {
|
|
9
|
+
private formatter: ILogFormatter;
|
|
10
|
+
private logger: ILogger;
|
|
11
|
+
|
|
12
|
+
constructor(formatter?: ILogFormatter, logger?: ILogger) {
|
|
13
|
+
this.formatter = formatter || new ConsoleLogFormatter();
|
|
14
|
+
this.logger = logger || SilentLogger;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async write(entry: ILogEntry): Promise<void> {
|
|
18
|
+
const formatted = this.formatter.format(entry);
|
|
19
|
+
|
|
20
|
+
const level: TLogLevel = entry.level;
|
|
21
|
+
switch (level) {
|
|
22
|
+
case 'debug':
|
|
23
|
+
this.logger.debug(formatted);
|
|
24
|
+
break;
|
|
25
|
+
case 'info':
|
|
26
|
+
this.logger.info(formatted);
|
|
27
|
+
break;
|
|
28
|
+
case 'warn':
|
|
29
|
+
this.logger.warn(formatted);
|
|
30
|
+
break;
|
|
31
|
+
case 'error':
|
|
32
|
+
this.logger.error(formatted);
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async flush(): Promise<void> {
|
|
38
|
+
// Console doesn't need flushing
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async close(): Promise<void> {
|
|
42
|
+
// Console doesn't need closing
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ILogEntry, ILogStorage, ILogFormatter } from '../types';
|
|
2
|
+
import { JsonLogFormatter } from '../formatters';
|
|
3
|
+
import { createLogger, type ILogger, PluginError } from '@robota-sdk/agent-core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* File log storage (placeholder implementation)
|
|
7
|
+
*/
|
|
8
|
+
export class FileLogStorage implements ILogStorage {
|
|
9
|
+
private filePath: string;
|
|
10
|
+
private formatter: ILogFormatter;
|
|
11
|
+
private logger: ILogger;
|
|
12
|
+
|
|
13
|
+
constructor(filePath: string, formatter?: ILogFormatter) {
|
|
14
|
+
this.filePath = filePath;
|
|
15
|
+
this.formatter = formatter || new JsonLogFormatter();
|
|
16
|
+
this.logger = createLogger('FileLogStorage');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async write(entry: ILogEntry): Promise<void> {
|
|
20
|
+
try {
|
|
21
|
+
// File writing would be implemented here
|
|
22
|
+
// This is a placeholder for actual file system operations
|
|
23
|
+
this.logger.warn('File logging not fully implemented yet', {
|
|
24
|
+
filePath: this.filePath,
|
|
25
|
+
entry: this.formatter.format(entry),
|
|
26
|
+
});
|
|
27
|
+
} catch (error) {
|
|
28
|
+
throw new PluginError('Failed to write log to file', 'LoggingPlugin', {
|
|
29
|
+
filePath: this.filePath,
|
|
30
|
+
error: error instanceof Error ? error.message : String(error),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async flush(): Promise<void> {
|
|
36
|
+
// File flushing would be implemented here
|
|
37
|
+
this.logger.warn('File flush not fully implemented yet');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async close(): Promise<void> {
|
|
41
|
+
// File closing would be implemented here
|
|
42
|
+
this.logger.warn('File close not fully implemented yet');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { ILogEntry, ILogStorage, ILogFormatter } from '../types';
|
|
2
|
+
import { JsonLogFormatter } from '../formatters';
|
|
3
|
+
import {
|
|
4
|
+
createLogger,
|
|
5
|
+
type ILogger,
|
|
6
|
+
PluginError,
|
|
7
|
+
type TTimerId,
|
|
8
|
+
startPeriodicTask,
|
|
9
|
+
stopPeriodicTask,
|
|
10
|
+
} from '@robota-sdk/agent-core';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Remote log storage with batching
|
|
14
|
+
*/
|
|
15
|
+
export class RemoteLogStorage implements ILogStorage {
|
|
16
|
+
private url: string;
|
|
17
|
+
private formatter: ILogFormatter;
|
|
18
|
+
private batchSize: number;
|
|
19
|
+
private flushInterval: number;
|
|
20
|
+
private pendingLogs: ILogEntry[] = [];
|
|
21
|
+
private flushTimer: TTimerId | undefined;
|
|
22
|
+
private logger: ILogger;
|
|
23
|
+
|
|
24
|
+
constructor(url: string, _options: { timeout?: number } = {}) {
|
|
25
|
+
this.url = url;
|
|
26
|
+
this.formatter = new JsonLogFormatter();
|
|
27
|
+
this.batchSize = 10;
|
|
28
|
+
this.flushInterval = 5000;
|
|
29
|
+
this.logger = createLogger('RemoteLogStorage');
|
|
30
|
+
|
|
31
|
+
// Start flush timer
|
|
32
|
+
this.flushTimer = startPeriodicTask(
|
|
33
|
+
this.logger,
|
|
34
|
+
{ name: 'RemoteLogStorage.flush', intervalMs: this.flushInterval },
|
|
35
|
+
async () => {
|
|
36
|
+
await this.flush();
|
|
37
|
+
},
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async write(entry: ILogEntry): Promise<void> {
|
|
42
|
+
this.pendingLogs.push(entry);
|
|
43
|
+
|
|
44
|
+
if (this.pendingLogs.length >= this.batchSize) {
|
|
45
|
+
await this.flush();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async flush(): Promise<void> {
|
|
50
|
+
if (this.pendingLogs.length === 0) return;
|
|
51
|
+
|
|
52
|
+
const logsToSend = [...this.pendingLogs];
|
|
53
|
+
this.pendingLogs = [];
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
// Remote sending would be implemented here
|
|
57
|
+
// This is a placeholder for actual HTTP requests
|
|
58
|
+
this.logger.warn('Remote logging not fully implemented yet', {
|
|
59
|
+
url: this.url,
|
|
60
|
+
logCount: logsToSend.length,
|
|
61
|
+
logs: logsToSend.map((log) => this.formatter.format(log)),
|
|
62
|
+
});
|
|
63
|
+
} catch (error) {
|
|
64
|
+
throw new PluginError('Failed to send logs to remote endpoint', 'LoggingPlugin', {
|
|
65
|
+
url: this.url,
|
|
66
|
+
logCount: logsToSend.length,
|
|
67
|
+
error: error instanceof Error ? error.message : String(error),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async close(): Promise<void> {
|
|
73
|
+
stopPeriodicTask(this.flushTimer);
|
|
74
|
+
this.flushTimer = undefined;
|
|
75
|
+
|
|
76
|
+
await this.flush();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ILogEntry, ILogStorage } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Silent log storage (no-op)
|
|
5
|
+
*/
|
|
6
|
+
export class SilentLogStorage implements ILogStorage {
|
|
7
|
+
async write(_entry: ILogEntry): Promise<void> {
|
|
8
|
+
// Silent mode - do nothing
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async flush(): Promise<void> {
|
|
12
|
+
// Silent mode - do nothing
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async close(): Promise<void> {
|
|
16
|
+
// Silent mode - do nothing
|
|
17
|
+
}
|
|
18
|
+
}
|