@smythos/sre 1.6.14 → 1.7.1
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/CHANGELOG +15 -0
- package/dist/index.js +52 -46
- package/dist/index.js.map +1 -1
- package/dist/types/Components/APIEndpoint.class.d.ts +2 -8
- package/dist/types/Components/Component.class.d.ts +9 -0
- package/dist/types/Components/Triggers/Gmail.trigger.d.ts +0 -17
- package/dist/types/Components/Triggers/JobScheduler.trigger.d.ts +10 -0
- package/dist/types/Components/Triggers/Trigger.class.d.ts +11 -0
- package/dist/types/Components/index.d.ts +6 -0
- package/dist/types/Core/Connector.class.d.ts +1 -0
- package/dist/types/Core/ConnectorsService.d.ts +2 -0
- package/dist/types/Core/HookService.d.ts +1 -1
- package/dist/types/helpers/Conversation.helper.d.ts +2 -0
- package/dist/types/helpers/Crypto.helper.d.ts +8 -0
- package/dist/types/index.d.ts +13 -0
- package/dist/types/subsystems/AgentManager/Agent.class.d.ts +4 -2
- package/dist/types/subsystems/AgentManager/AgentData.service/AgentDataConnector.d.ts +13 -0
- package/dist/types/subsystems/AgentManager/AgentData.service/connectors/NullAgentData.class.d.ts +1 -4
- package/dist/types/subsystems/AgentManager/Scheduler.service/Job.class.d.ts +29 -6
- package/dist/types/subsystems/AgentManager/Scheduler.service/SchedulerConnector.d.ts +11 -3
- package/dist/types/subsystems/AgentManager/Scheduler.service/connectors/LocalScheduler.class.d.ts +31 -7
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.d.ts +2 -5
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.d.ts +3 -6
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.d.ts +7 -0
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/xAI.class.d.ts +2 -5
- package/dist/types/types/Agent.types.d.ts +1 -0
- package/dist/types/types/LLM.types.d.ts +2 -5
- package/dist/types/types/SRE.types.d.ts +4 -1
- package/package.json +6 -2
- package/src/Components/APICall/OAuth.helper.ts +30 -35
- package/src/Components/APIEndpoint.class.ts +25 -6
- package/src/Components/Component.class.ts +11 -0
- package/src/Components/Triggers/Gmail.trigger.ts +282 -0
- package/src/Components/Triggers/JobScheduler.trigger.ts +45 -0
- package/src/Components/Triggers/README.md +3 -0
- package/src/Components/Triggers/Trigger.class.ts +101 -0
- package/src/Components/Triggers/WhatsApp.trigger.ts +219 -0
- package/src/Components/index.ts +8 -0
- package/src/Core/AgentProcess.helper.ts +4 -6
- package/src/Core/Connector.class.ts +11 -3
- package/src/Core/ConnectorsService.ts +5 -0
- package/src/Core/ExternalEventsReceiver.ts +317 -0
- package/src/Core/HookService.ts +20 -6
- package/src/Core/SmythRuntime.class.ts +7 -0
- package/src/Core/SystemEvents.ts +17 -0
- package/src/Core/boot.ts +2 -0
- package/src/helpers/Conversation.helper.ts +35 -11
- package/src/helpers/Crypto.helper.ts +28 -0
- package/src/index.ts +208 -195
- package/src/index.ts.bak +208 -195
- package/src/subsystems/AGENTS.md +594 -0
- package/src/subsystems/AgentManager/Agent.class.ts +71 -21
- package/src/subsystems/AgentManager/AgentData.service/AgentDataConnector.ts +24 -1
- package/src/subsystems/AgentManager/AgentData.service/connectors/NullAgentData.class.ts +2 -2
- package/src/subsystems/AgentManager/AgentRuntime.class.ts +34 -5
- package/src/subsystems/AgentManager/Scheduler.service/Job.class.ts +414 -0
- package/src/subsystems/AgentManager/Scheduler.service/Schedule.class.ts +200 -0
- package/src/subsystems/AgentManager/Scheduler.service/SchedulerConnector.ts +200 -0
- package/src/subsystems/AgentManager/Scheduler.service/connectors/LocalScheduler.class.ts +767 -0
- package/src/subsystems/AgentManager/Scheduler.service/index.ts +11 -0
- package/src/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.ts +1 -1
- package/src/subsystems/LLMManager/LLM.service/LLMCredentials.helper.ts +61 -2
- package/src/subsystems/LLMManager/LLM.service/connectors/Anthropic.class.ts +3 -0
- package/src/subsystems/LLMManager/LLM.service/connectors/Bedrock.class.ts +3 -1
- package/src/subsystems/LLMManager/LLM.service/connectors/Echo.class.ts +5 -1
- package/src/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.ts +247 -56
- package/src/subsystems/LLMManager/LLM.service/connectors/Groq.class.ts +3 -0
- package/src/subsystems/LLMManager/LLM.service/connectors/Ollama.class.ts +28 -21
- package/src/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.ts +3 -0
- package/src/subsystems/LLMManager/LLM.service/connectors/VertexAI.class.ts +121 -33
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.ts +38 -27
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts +115 -18
- package/src/subsystems/LLMManager/LLM.service/connectors/xAI.class.ts +3 -0
- package/src/subsystems/LLMManager/ModelsProvider.service/ModelsProviderConnector.ts +1 -6
- package/src/subsystems/MemoryManager/LLMContext.ts +3 -8
- package/src/subsystems/MemoryManager/RuntimeContext.ts +10 -9
- package/src/subsystems/Security/Credentials/Credentials.class.ts +1 -0
- package/src/subsystems/Security/Credentials/ManagedOAuth2Credentials.class.ts +106 -0
- package/src/types/Agent.types.ts +1 -0
- package/src/types/LLM.types.ts +2 -2
- package/src/types/SRE.types.ts +3 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { createServer, IncomingMessage, ServerResponse } from 'http';
|
|
2
|
+
import { WebSocket, WebSocketServer } from 'ws';
|
|
3
|
+
import { SystemEvents } from './SystemEvents';
|
|
4
|
+
import { Logger } from '../helpers/Log.helper';
|
|
5
|
+
import { ConnectorService } from './ConnectorsService';
|
|
6
|
+
import { Connector } from './Connector.class';
|
|
7
|
+
import { TConnectorService } from '@sre/types/SRE.types';
|
|
8
|
+
import { validateServiceKey } from '@sre/helpers/Crypto.helper';
|
|
9
|
+
|
|
10
|
+
const logger = Logger('ExternalEventsReceiver');
|
|
11
|
+
|
|
12
|
+
export interface ExternalEventsReceiverConfig {
|
|
13
|
+
port: number;
|
|
14
|
+
authTokens: string[]; // List of valid authentication tokens
|
|
15
|
+
enableHttp?: boolean; // Enable HTTP endpoint (default: true)
|
|
16
|
+
enableWebSocket?: boolean; // Enable WebSocket endpoint (default: true)
|
|
17
|
+
path?: string; // WebSocket path (default: '/ws')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class ExternalEventsReceiver {
|
|
21
|
+
private server: ReturnType<typeof createServer> | null = null;
|
|
22
|
+
private wss: WebSocketServer | null = null;
|
|
23
|
+
private config: Required<ExternalEventsReceiverConfig>;
|
|
24
|
+
private isRunning = false;
|
|
25
|
+
|
|
26
|
+
constructor(config: ExternalEventsReceiverConfig) {
|
|
27
|
+
this.config = {
|
|
28
|
+
enableHttp: true,
|
|
29
|
+
enableWebSocket: true,
|
|
30
|
+
path: '/ws',
|
|
31
|
+
...config,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
if (!this.config.authTokens || this.config.authTokens.length === 0) {
|
|
35
|
+
logger.warn('At least one authentication token must be provided');
|
|
36
|
+
throw new Error('At least one authentication token must be provided');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Start the HTTP and WebSocket server
|
|
42
|
+
*/
|
|
43
|
+
public async start(): Promise<void> {
|
|
44
|
+
if (this.isRunning) {
|
|
45
|
+
logger.warn('Server is already running');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Create HTTP server
|
|
50
|
+
this.server = createServer((req, res) => this.handleHttpRequest(req, res));
|
|
51
|
+
|
|
52
|
+
// Create WebSocket server if enabled
|
|
53
|
+
if (this.config.enableWebSocket) {
|
|
54
|
+
this.wss = new WebSocketServer({
|
|
55
|
+
server: this.server,
|
|
56
|
+
path: this.config.path,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
this.wss.on('connection', (ws: WebSocket, req: IncomingMessage) => {
|
|
60
|
+
this.handleWebSocketConnection(ws, req);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return new Promise((resolve, reject) => {
|
|
65
|
+
this.server!.listen(this.config.port, '127.0.0.1', () => {
|
|
66
|
+
this.isRunning = true;
|
|
67
|
+
logger.debug(
|
|
68
|
+
`Server started on port ${this.config.port}` +
|
|
69
|
+
(this.config.enableHttp ? ' [HTTP]' : '') +
|
|
70
|
+
(this.config.enableWebSocket ? ` [WebSocket: ${this.config.path}]` : '')
|
|
71
|
+
);
|
|
72
|
+
resolve();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
this.server!.on('error', (error) => {
|
|
76
|
+
logger.error('Server error', error);
|
|
77
|
+
reject(error);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Stop the server
|
|
84
|
+
*/
|
|
85
|
+
public async stop(): Promise<void> {
|
|
86
|
+
if (!this.isRunning) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
// Close WebSocket server first
|
|
92
|
+
if (this.wss) {
|
|
93
|
+
this.wss.close((err) => {
|
|
94
|
+
if (err) {
|
|
95
|
+
logger.error('Error closing WebSocket server', err);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Close HTTP server
|
|
101
|
+
if (this.server) {
|
|
102
|
+
this.server.close((err) => {
|
|
103
|
+
if (err) {
|
|
104
|
+
logger.error('Error closing HTTP server', err);
|
|
105
|
+
reject(err);
|
|
106
|
+
} else {
|
|
107
|
+
this.isRunning = false;
|
|
108
|
+
logger.debug('Server stopped');
|
|
109
|
+
resolve();
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
} else {
|
|
113
|
+
resolve();
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Handle HTTP requests
|
|
120
|
+
*/
|
|
121
|
+
private handleHttpRequest(req: IncomingMessage, res: ServerResponse): void {
|
|
122
|
+
if (!this.config.enableHttp) {
|
|
123
|
+
this.sendResponse(res, 404, { error: 'HTTP endpoint is disabled' });
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Only accept POST requests
|
|
128
|
+
if (req.method !== 'POST') {
|
|
129
|
+
this.sendResponse(res, 405, { error: 'Method not allowed. Only POST requests are accepted.' });
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Validate authentication
|
|
134
|
+
const authResult = this.validateAuth(req.headers);
|
|
135
|
+
if (!authResult.valid) {
|
|
136
|
+
this.sendResponse(res, 401, { error: authResult.error });
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Get connector name from header
|
|
141
|
+
const connectorName = this.getConnectorName(req.headers);
|
|
142
|
+
if (!connectorName) {
|
|
143
|
+
this.sendResponse(res, 400, { error: 'Missing x-connector-name header' });
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Read request body
|
|
148
|
+
let body = '';
|
|
149
|
+
req.on('data', (chunk) => {
|
|
150
|
+
body += chunk.toString();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
req.on('end', () => {
|
|
154
|
+
try {
|
|
155
|
+
const data = body ? JSON.parse(body) : {};
|
|
156
|
+
|
|
157
|
+
// Emit event
|
|
158
|
+
this.emitExternalEvent(connectorName, data);
|
|
159
|
+
|
|
160
|
+
this.sendResponse(res, 200, {
|
|
161
|
+
success: true,
|
|
162
|
+
message: `Event EXT:${connectorName} emitted successfully`,
|
|
163
|
+
});
|
|
164
|
+
} catch (error) {
|
|
165
|
+
logger.error('Error processing HTTP request', error);
|
|
166
|
+
this.sendResponse(res, 400, {
|
|
167
|
+
error: 'Invalid JSON payload',
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
req.on('error', (error) => {
|
|
173
|
+
logger.error('HTTP request error', error);
|
|
174
|
+
this.sendResponse(res, 500, { error: 'Internal server error' });
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Handle WebSocket connections
|
|
180
|
+
*/
|
|
181
|
+
private handleWebSocketConnection(ws: WebSocket, req: IncomingMessage): void {
|
|
182
|
+
logger.debug('New WebSocket connection');
|
|
183
|
+
|
|
184
|
+
// Validate authentication from headers
|
|
185
|
+
const authResult = this.validateAuth(req.headers);
|
|
186
|
+
if (!authResult.valid) {
|
|
187
|
+
ws.close(1008, authResult.error);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Get connector name from headers
|
|
192
|
+
let connectorName = this.getConnectorName(req.headers);
|
|
193
|
+
|
|
194
|
+
ws.on('message', (message: Buffer) => {
|
|
195
|
+
try {
|
|
196
|
+
const data = JSON.parse(message.toString());
|
|
197
|
+
|
|
198
|
+
// If connector name not in headers, check message
|
|
199
|
+
if (!connectorName) {
|
|
200
|
+
connectorName = data.connectorName || data['x-connector-name'];
|
|
201
|
+
|
|
202
|
+
if (!connectorName) {
|
|
203
|
+
ws.send(
|
|
204
|
+
JSON.stringify({
|
|
205
|
+
error: 'Missing connector name in headers or message',
|
|
206
|
+
})
|
|
207
|
+
);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Emit event
|
|
213
|
+
this.emitExternalEvent(connectorName, data);
|
|
214
|
+
|
|
215
|
+
// Send acknowledgment
|
|
216
|
+
ws.send(
|
|
217
|
+
JSON.stringify({
|
|
218
|
+
success: true,
|
|
219
|
+
message: `Event EXT:${connectorName} emitted successfully`,
|
|
220
|
+
})
|
|
221
|
+
);
|
|
222
|
+
} catch (error) {
|
|
223
|
+
logger.error('Error processing WebSocket message', error);
|
|
224
|
+
ws.send(
|
|
225
|
+
JSON.stringify({
|
|
226
|
+
error: 'Invalid JSON message',
|
|
227
|
+
})
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
ws.on('error', (error) => {
|
|
233
|
+
logger.error('WebSocket error', error);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
ws.on('close', () => {
|
|
237
|
+
logger.debug('WebSocket connection closed');
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Validate authentication token
|
|
243
|
+
*/
|
|
244
|
+
private validateAuth(headers: IncomingMessage['headers']): {
|
|
245
|
+
valid: boolean;
|
|
246
|
+
serviceName?: string;
|
|
247
|
+
error?: string;
|
|
248
|
+
} {
|
|
249
|
+
const authHeader = headers['authorization'] || headers['Authorization'];
|
|
250
|
+
const serviceName = headers['x-service-name'] || headers['X-Service-Name'];
|
|
251
|
+
|
|
252
|
+
if (!authHeader) {
|
|
253
|
+
logger.warn('Missing Authorization header');
|
|
254
|
+
return { valid: false, error: 'Missing Authorization header' };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (!serviceName || typeof serviceName !== 'string') {
|
|
258
|
+
logger.warn('Missing or invalid X-Service-Name header');
|
|
259
|
+
return { valid: false, error: 'Missing X-Service-Name header' };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Extract token
|
|
263
|
+
const token = (authHeader as string).replace(/^Bearer\s+/i, '');
|
|
264
|
+
|
|
265
|
+
if (!token) {
|
|
266
|
+
return { valid: false, error: 'Invalid Authorization header format' };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Validate using service key derivation
|
|
270
|
+
if (!validateServiceKey(serviceName, token)) {
|
|
271
|
+
logger.warn(`Invalid key for service: ${serviceName}`);
|
|
272
|
+
return { valid: false, error: 'Invalid authentication' };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
logger.debug(`Authenticated service: ${serviceName}`);
|
|
276
|
+
return { valid: true, serviceName };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Extract connector name from headers
|
|
281
|
+
*/
|
|
282
|
+
private getConnectorName(headers: IncomingMessage['headers']): string | null {
|
|
283
|
+
const connectorName = headers['x-connector-name'] || headers['X-Connector-Name'];
|
|
284
|
+
return typeof connectorName === 'string' ? connectorName : connectorName?.[0] || null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Emit external connector event
|
|
289
|
+
*/
|
|
290
|
+
private emitExternalEvent(connectorName: string, data: any): void {
|
|
291
|
+
const eventName = `EXT:${connectorName}` as const;
|
|
292
|
+
const connector = ConnectorService.getInstance<Connector>(TConnectorService[connectorName]);
|
|
293
|
+
if (connector) {
|
|
294
|
+
connector.handleEvent(eventName, data);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Send HTTP response
|
|
300
|
+
*/
|
|
301
|
+
private sendResponse(res: ServerResponse, statusCode: number, body: any): void {
|
|
302
|
+
res.statusCode = statusCode;
|
|
303
|
+
res.setHeader('Content-Type', 'application/json');
|
|
304
|
+
res.end(JSON.stringify(body));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Get server status
|
|
309
|
+
*/
|
|
310
|
+
public getStatus(): { running: boolean; port: number; config: Required<ExternalEventsReceiverConfig> } {
|
|
311
|
+
return {
|
|
312
|
+
running: this.isRunning,
|
|
313
|
+
port: this.config.port,
|
|
314
|
+
config: this.config,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
}
|
package/src/Core/HookService.ts
CHANGED
|
@@ -118,14 +118,26 @@ export function hook(hookName: string) {
|
|
|
118
118
|
* Decorator function that executes registered hooks asynchronously before and after the decorated method
|
|
119
119
|
* @param hookName The name of the hook to trigger
|
|
120
120
|
*/
|
|
121
|
-
export function hookAsync(hookName: string) {
|
|
121
|
+
export function hookAsync(hookName: string, customContext?: Record<string, any> | ((instance: any) => Record<string, any>)) {
|
|
122
122
|
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
|
123
123
|
const originalMethod = descriptor.value;
|
|
124
124
|
|
|
125
125
|
descriptor.value = async function (...args: any[]) {
|
|
126
|
+
// Get additional context if contextFn is provided
|
|
127
|
+
let _context;
|
|
128
|
+
if (typeof customContext === 'function') {
|
|
129
|
+
_context = await customContext(this);
|
|
130
|
+
} else if (typeof customContext === 'object') {
|
|
131
|
+
_context = customContext;
|
|
132
|
+
} else {
|
|
133
|
+
_context = this;
|
|
134
|
+
}
|
|
135
|
+
|
|
126
136
|
// Execute non-blocking pre-hooks first (fire and forget)
|
|
127
137
|
if (nonBlockingHooks[hookName]) {
|
|
128
|
-
void Promise.allSettled(nonBlockingHooks[hookName].map((callback) => Promise.resolve(
|
|
138
|
+
void Promise.allSettled(nonBlockingHooks[hookName].map((callback) => Promise.resolve(callback.apply(_context, args)))).catch((err) =>
|
|
139
|
+
console.error(`Non-blocking hook ${hookName} error:`, err)
|
|
140
|
+
);
|
|
129
141
|
}
|
|
130
142
|
|
|
131
143
|
let result: any;
|
|
@@ -134,7 +146,7 @@ export function hookAsync(hookName: string) {
|
|
|
134
146
|
try {
|
|
135
147
|
// Execute blocking pre-hooks and wait for them
|
|
136
148
|
if (blockingHooks[hookName]) {
|
|
137
|
-
await Promise.all(blockingHooks[hookName].map((callback) => Promise.resolve(callback.apply(
|
|
149
|
+
await Promise.all(blockingHooks[hookName].map((callback) => Promise.resolve(callback.apply(_context, args))));
|
|
138
150
|
}
|
|
139
151
|
|
|
140
152
|
// Call the original method
|
|
@@ -148,13 +160,15 @@ export function hookAsync(hookName: string) {
|
|
|
148
160
|
// Execute non-blocking after-hooks first (fire and forget)
|
|
149
161
|
if (nonBlockingAfterHooks[hookName]) {
|
|
150
162
|
void Promise.allSettled(
|
|
151
|
-
nonBlockingAfterHooks[hookName].map((callback) => Promise.resolve(
|
|
152
|
-
);
|
|
163
|
+
nonBlockingAfterHooks[hookName].map((callback) => Promise.resolve(callback.apply(_context, [{ result, args, error }])))
|
|
164
|
+
).catch((err) => console.error(`Non-blocking after-hook ${hookName} error:`, err));
|
|
153
165
|
}
|
|
154
166
|
|
|
155
167
|
// Execute blocking after-hooks and wait for them
|
|
156
168
|
if (blockingAfterHooks[hookName]) {
|
|
157
|
-
await Promise.all(
|
|
169
|
+
await Promise.all(
|
|
170
|
+
blockingAfterHooks[hookName].map((callback) => Promise.resolve(callback.apply(_context, [{ result, args, error }])))
|
|
171
|
+
);
|
|
158
172
|
}
|
|
159
173
|
} catch (afterHookError) {
|
|
160
174
|
// Log after-hook errors but don't let them override the original error
|
package/src/Core/SystemEvents.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { SmythLLMUsage, SmythTaskUsage } from '@sre/types/LLM.types';
|
|
2
2
|
import { TServiceRegistry } from '@sre/types/SRE.types';
|
|
3
3
|
import { EventEmitter } from 'events';
|
|
4
|
+
import { ExternalEventsReceiver } from './ExternalEventsReceiver';
|
|
5
|
+
import { createHash } from 'crypto';
|
|
6
|
+
import { Logger } from '../helpers/Log.helper';
|
|
7
|
+
|
|
8
|
+
const logger = Logger('SystemEvents');
|
|
4
9
|
|
|
5
10
|
export type SystemEventMap = {
|
|
6
11
|
'SRE:BootStart': [];
|
|
@@ -13,4 +18,16 @@ export type SystemEventMap = {
|
|
|
13
18
|
|
|
14
19
|
const SystemEvents = new EventEmitter<SystemEventMap>();
|
|
15
20
|
|
|
21
|
+
// /!\ incomplete implementation, do not enable yet
|
|
22
|
+
// if (process.env?.SRE_SECRET?.trim()) {
|
|
23
|
+
// const secretHash = createHash('sha256').update(process.env.SRE_SECRET).digest('hex');
|
|
24
|
+
// // Create server instance
|
|
25
|
+
// new ExternalEventsReceiver({
|
|
26
|
+
// port: process.env.SRE_PORT ? parseInt(process.env.SRE_PORT) : 55555,
|
|
27
|
+
// authTokens: [secretHash],
|
|
28
|
+
// });
|
|
29
|
+
// } else {
|
|
30
|
+
// logger.warn('SRE_SECRET is not set, external events receiver will not be started');
|
|
31
|
+
// }
|
|
32
|
+
|
|
16
33
|
export { SystemEvents };
|
package/src/Core/boot.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { LogService } from '@sre/IO/Log.service';
|
|
|
16
16
|
import { ComponentService } from '@sre/AgentManager/Component.service';
|
|
17
17
|
import { ModelsProviderService } from '@sre/LLMManager/ModelsProvider.service';
|
|
18
18
|
import { CodeService } from '@sre/ComputeManager/Code.service';
|
|
19
|
+
import { SchedulerService } from '@sre/AgentManager/Scheduler.service';
|
|
19
20
|
const console = Logger('Boot');
|
|
20
21
|
let _booted = false;
|
|
21
22
|
export function boot() {
|
|
@@ -42,6 +43,7 @@ export function boot() {
|
|
|
42
43
|
service.Log = new LogService();
|
|
43
44
|
service.Component = new ComponentService();
|
|
44
45
|
service.Code = new CodeService();
|
|
46
|
+
service.Scheduler = new SchedulerService();
|
|
45
47
|
|
|
46
48
|
SystemEvents.on('SRE:Initialized', () => {
|
|
47
49
|
console.debug('SRE Initialized');
|
|
@@ -80,6 +80,7 @@ export class Conversation extends EventEmitter {
|
|
|
80
80
|
public set spec(specSource) {
|
|
81
81
|
this.ready.then(() => {
|
|
82
82
|
this._status = '';
|
|
83
|
+
this._currentWaitPromise = undefined;
|
|
83
84
|
this.loadSpecFromSource(specSource).then(async (spec) => {
|
|
84
85
|
if (!spec) {
|
|
85
86
|
this._status = 'error';
|
|
@@ -783,19 +784,24 @@ export class Conversation extends EventEmitter {
|
|
|
783
784
|
const _arguments: any = {};
|
|
784
785
|
for (let arg of openApiArgs) {
|
|
785
786
|
_arguments[arg.name] = arg.schema;
|
|
786
|
-
if (tool.inputs
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
arg.schema.properties[prop].description
|
|
787
|
+
if (tool.inputs) {
|
|
788
|
+
if (arg.schema.properties) {
|
|
789
|
+
const required = [];
|
|
790
|
+
for (let prop in arg.schema.properties) {
|
|
791
|
+
const input = tool.inputs?.find((i) => i.name === prop);
|
|
792
|
+
if (!arg.schema.properties[prop].description) {
|
|
793
|
+
arg.schema.properties[prop].description = input?.description;
|
|
794
|
+
}
|
|
795
|
+
if (!input?.optional) {
|
|
796
|
+
required.push(prop);
|
|
797
|
+
}
|
|
792
798
|
}
|
|
793
|
-
if (
|
|
794
|
-
required
|
|
799
|
+
if (required.length) {
|
|
800
|
+
arg.schema.required = required;
|
|
795
801
|
}
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
arg.schema.
|
|
802
|
+
} else {
|
|
803
|
+
const input = tool.inputs?.find((i) => i.name === arg.name);
|
|
804
|
+
arg.schema.description = input?.description;
|
|
799
805
|
}
|
|
800
806
|
}
|
|
801
807
|
}
|
|
@@ -847,6 +853,23 @@ export class Conversation extends EventEmitter {
|
|
|
847
853
|
|
|
848
854
|
this._toolsConfig = toolsConfig;
|
|
849
855
|
}
|
|
856
|
+
|
|
857
|
+
async removeTool(toolName: string) {
|
|
858
|
+
this._customToolsDeclarations = this._customToolsDeclarations.filter((tool) => tool.name !== toolName);
|
|
859
|
+
delete this._customToolsHandlers[toolName];
|
|
860
|
+
const llmInference: LLMInference = await LLMInference.getInstance(this.model, AccessCandidate.team(this._teamId));
|
|
861
|
+
|
|
862
|
+
const toolsConfig: any = llmInference.connector.formatToolsConfig({
|
|
863
|
+
type: 'function',
|
|
864
|
+
toolDefinitions: this._customToolsDeclarations,
|
|
865
|
+
toolChoice: this.toolChoice,
|
|
866
|
+
});
|
|
867
|
+
this._toolsConfig = toolsConfig;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
public get toolNames() {
|
|
871
|
+
return this._customToolsDeclarations.map((tool) => tool.name);
|
|
872
|
+
}
|
|
850
873
|
/**
|
|
851
874
|
* updates LLM model, if spec is available, it will update the tools config
|
|
852
875
|
* @param model
|
|
@@ -928,6 +951,7 @@ export class Conversation extends EventEmitter {
|
|
|
928
951
|
//is this a valid agent data?
|
|
929
952
|
if (typeof specSource?.behavior === 'string' && specSource?.components && specSource?.connections) {
|
|
930
953
|
this.agentData = specSource; //agent loaded from data directly
|
|
954
|
+
this._specSource = specSource;
|
|
931
955
|
this._agentId = specSource.id;
|
|
932
956
|
return await this.loadSpecFromAgent(specSource);
|
|
933
957
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from 'crypto';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generate a service-specific authentication key
|
|
5
|
+
*/
|
|
6
|
+
export function generateServiceKey(serviceName: string): string {
|
|
7
|
+
const secret = process.env.SRE_SECRET;
|
|
8
|
+
|
|
9
|
+
if (!secret || secret.trim().length < 32) {
|
|
10
|
+
throw new Error('SRE_SECRET must be at least 32 characters');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return createHmac('sha256', secret).update(`sre-service:${serviceName}`).digest('hex');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Validate a service key
|
|
18
|
+
*/
|
|
19
|
+
export function validateServiceKey(serviceName: string, providedKey: string): boolean {
|
|
20
|
+
try {
|
|
21
|
+
const expectedKey = generateServiceKey(serviceName);
|
|
22
|
+
|
|
23
|
+
// Constant-time comparison to prevent timing attacks
|
|
24
|
+
return timingSafeEqual(Buffer.from(expectedKey), Buffer.from(providedKey));
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|