@smythos/sre 1.6.14 → 1.7.5
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 +66 -58
- 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/BinaryInput.helper.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/helpers/LocalCache.helper.d.ts +18 -0
- package/dist/types/helpers/TemplateString.helper.d.ts +2 -1
- 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/IO/VectorDB.service/VectorDBConnector.d.ts +4 -4
- package/dist/types/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.d.ts +2 -2
- package/dist/types/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.d.ts +2 -2
- package/dist/types/subsystems/IO/VectorDB.service/connectors/RAMVecrtorDB.class.d.ts +2 -2
- package/dist/types/subsystems/IO/VectorDB.service/embed/BaseEmbedding.d.ts +16 -9
- package/dist/types/subsystems/IO/VectorDB.service/embed/index.d.ts +4 -1
- package/dist/types/subsystems/LLMManager/LLM.inference.d.ts +36 -2
- 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 +56 -36
- package/dist/types/types/SRE.types.d.ts +4 -1
- package/dist/types/types/VectorDB.types.d.ts +6 -3
- package/dist/types/utils/string.utils.d.ts +0 -4
- 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/Classifier.class.ts +8 -2
- package/src/Components/Component.class.ts +11 -0
- package/src/Components/GenAILLM.class.ts +11 -7
- package/src/Components/LLMAssistant.class.ts +12 -3
- package/src/Components/ScrapflyWebScrape.class.ts +8 -1
- package/src/Components/TavilyWebSearch.class.ts +4 -1
- 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 +20 -2
- package/src/Core/SystemEvents.ts +17 -0
- package/src/Core/boot.ts +2 -0
- package/src/helpers/BinaryInput.helper.ts +8 -8
- package/src/helpers/Conversation.helper.ts +46 -12
- package/src/helpers/Crypto.helper.ts +28 -0
- package/src/helpers/LocalCache.helper.ts +18 -0
- package/src/helpers/TemplateString.helper.ts +20 -9
- package/src/index.ts +13 -0
- package/src/index.ts.bak +13 -0
- package/src/subsystems/AGENTS.md +594 -0
- package/src/subsystems/AgentManager/Agent.class.ts +73 -21
- package/src/subsystems/AgentManager/AgentData.service/AgentDataConnector.ts +30 -6
- package/src/subsystems/AgentManager/AgentData.service/connectors/NullAgentData.class.ts +2 -2
- package/src/subsystems/AgentManager/AgentLogger.class.ts +1 -1
- 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/VectorDBConnector.ts +15 -4
- package/src/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.ts +32 -11
- package/src/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.ts +27 -10
- package/src/subsystems/IO/VectorDB.service/connectors/RAMVecrtorDB.class.ts +25 -9
- package/src/subsystems/IO/VectorDB.service/embed/BaseEmbedding.ts +182 -12
- package/src/subsystems/IO/VectorDB.service/embed/GoogleEmbedding.ts +1 -1
- package/src/subsystems/IO/VectorDB.service/embed/OpenAIEmbedding.ts +1 -1
- package/src/subsystems/IO/VectorDB.service/embed/index.ts +12 -2
- package/src/subsystems/LLMManager/LLM.inference.ts +76 -17
- 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/ChatCompletionsApiInterface.ts +3 -2
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts +117 -20
- package/src/subsystems/LLMManager/LLM.service/connectors/xAI.class.ts +3 -0
- package/src/subsystems/LLMManager/ModelsProvider.service/ModelsProviderConnector.ts +3 -8
- package/src/subsystems/LLMManager/ModelsProvider.service/connectors/JSONModelsProvider.class.ts +4 -1
- package/src/subsystems/MemoryManager/Cache.service/connectors/RedisCache.class.ts +12 -0
- 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 +68 -40
- package/src/types/SRE.types.ts +3 -0
- package/src/types/VectorDB.types.ts +7 -3
- package/src/utils/string.utils.ts +193 -191
|
@@ -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
|
|
@@ -9,6 +9,8 @@ import pkg from '../../package.json';
|
|
|
9
9
|
|
|
10
10
|
const logger = Logger('SRE');
|
|
11
11
|
|
|
12
|
+
const SRE_GLOBAL_KEY = Symbol.for('SRE@singleton');
|
|
13
|
+
|
|
12
14
|
export class SmythRuntime {
|
|
13
15
|
public started = false;
|
|
14
16
|
|
|
@@ -67,6 +69,13 @@ export class SmythRuntime {
|
|
|
67
69
|
Router: {
|
|
68
70
|
Connector: 'NullRouter',
|
|
69
71
|
},
|
|
72
|
+
Scheduler: {
|
|
73
|
+
Connector: 'LocalScheduler',
|
|
74
|
+
Settings: {
|
|
75
|
+
runJobs: true,
|
|
76
|
+
persistExecutionHistory: true,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
70
79
|
};
|
|
71
80
|
|
|
72
81
|
protected constructor() {
|
|
@@ -77,10 +86,19 @@ export class SmythRuntime {
|
|
|
77
86
|
}
|
|
78
87
|
|
|
79
88
|
protected static instance?: SmythRuntime;
|
|
89
|
+
// public static get Instance(): SmythRuntime {
|
|
90
|
+
// if (!SmythRuntime.instance) {
|
|
91
|
+
// SmythRuntime.instance = new SmythRuntime();
|
|
92
|
+
// }
|
|
93
|
+
// return SmythRuntime.instance;
|
|
94
|
+
// }
|
|
95
|
+
|
|
80
96
|
public static get Instance(): SmythRuntime {
|
|
81
|
-
if (
|
|
82
|
-
|
|
97
|
+
if (global[SRE_GLOBAL_KEY]) {
|
|
98
|
+
return global[SRE_GLOBAL_KEY];
|
|
83
99
|
}
|
|
100
|
+
SmythRuntime.instance = new SmythRuntime();
|
|
101
|
+
global[SRE_GLOBAL_KEY] = SmythRuntime.instance;
|
|
84
102
|
return SmythRuntime.instance;
|
|
85
103
|
}
|
|
86
104
|
|
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');
|
|
@@ -23,7 +23,7 @@ export class BinaryInput {
|
|
|
23
23
|
data: BinaryInput | Buffer | ArrayBuffer | Blob | string | Record<string, any>,
|
|
24
24
|
private _name?: string,
|
|
25
25
|
public mimetype?: string,
|
|
26
|
-
private candidate?: IAccessCandidate
|
|
26
|
+
private candidate?: IAccessCandidate
|
|
27
27
|
) {
|
|
28
28
|
if (!_name) _name = uid();
|
|
29
29
|
this._name = _name;
|
|
@@ -97,6 +97,9 @@ export class BinaryInput {
|
|
|
97
97
|
this._ready = true;
|
|
98
98
|
}
|
|
99
99
|
} else {
|
|
100
|
+
//try to guess the mimetype from the url extension
|
|
101
|
+
const ext = this.url.split('.').pop();
|
|
102
|
+
this.mimetype = mime.getType(ext) || '';
|
|
100
103
|
this._ready = true;
|
|
101
104
|
}
|
|
102
105
|
return;
|
|
@@ -205,8 +208,6 @@ export class BinaryInput {
|
|
|
205
208
|
this._ready = true;
|
|
206
209
|
return;
|
|
207
210
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
211
|
}
|
|
211
212
|
|
|
212
213
|
private async getUrlInfo(url) {
|
|
@@ -221,9 +222,9 @@ export class BinaryInput {
|
|
|
221
222
|
}
|
|
222
223
|
}
|
|
223
224
|
|
|
224
|
-
public static from(
|
|
225
|
-
if (
|
|
226
|
-
return new BinaryInput(
|
|
225
|
+
public static from(source, name?: string, mimetype?: string, candidate?: IAccessCandidate) {
|
|
226
|
+
if (source instanceof BinaryInput) return source;
|
|
227
|
+
return new BinaryInput(source, name, mimetype, candidate);
|
|
227
228
|
}
|
|
228
229
|
|
|
229
230
|
public async upload(candidate: IAccessCandidate, ttl?: number) {
|
|
@@ -239,8 +240,7 @@ export class BinaryInput {
|
|
|
239
240
|
this.url = `smythfs://${teamId}.team/${candidate.id}/_temp/${this._name}`;
|
|
240
241
|
await SmythFS.Instance.write(this.url, this._source, candidate, undefined, ttl);
|
|
241
242
|
this._uploading = false;
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
243
|
+
} else {
|
|
244
244
|
this._uploading = false;
|
|
245
245
|
}
|
|
246
246
|
} catch (error) {
|
|
@@ -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';
|
|
@@ -223,6 +224,7 @@ export class Conversation extends EventEmitter {
|
|
|
223
224
|
teamId: instance._teamId,
|
|
224
225
|
agentId: instance._agentId,
|
|
225
226
|
model: instance._model,
|
|
227
|
+
agentData: instance.agentData,
|
|
226
228
|
};
|
|
227
229
|
})
|
|
228
230
|
public async prompt(message?: string | any, toolHeaders = {}, concurrentToolCalls = 4, abortSignal?: AbortSignal) {
|
|
@@ -249,6 +251,7 @@ export class Conversation extends EventEmitter {
|
|
|
249
251
|
teamId: instance._teamId,
|
|
250
252
|
agentId: instance._agentId,
|
|
251
253
|
model: instance._model,
|
|
254
|
+
agentData: instance.agentData,
|
|
252
255
|
};
|
|
253
256
|
})
|
|
254
257
|
public async streamPrompt(message?: string | any, toolHeaders = {}, concurrentToolCalls = 4, abortSignal?: AbortSignal) {
|
|
@@ -401,6 +404,11 @@ export class Conversation extends EventEmitter {
|
|
|
401
404
|
llmMessage.thinkingBlocks = thinkingBlocks;
|
|
402
405
|
}
|
|
403
406
|
|
|
407
|
+
//add tool status for every tool entry
|
|
408
|
+
toolsData.forEach((tool) => {
|
|
409
|
+
tool.status = tool.name ? this._toolStatusMap?.[tool.name] : undefined;
|
|
410
|
+
});
|
|
411
|
+
|
|
404
412
|
llmMessage.tool_calls = toolsData.map((tool) => {
|
|
405
413
|
return {
|
|
406
414
|
id: tool.id,
|
|
@@ -677,7 +685,10 @@ export class Conversation extends EventEmitter {
|
|
|
677
685
|
const requiresRemoteCall =
|
|
678
686
|
reqConfig.headers['X-DEBUG'] !== undefined ||
|
|
679
687
|
reqConfig.headers['X-MONITOR-ID'] !== undefined ||
|
|
680
|
-
|
|
688
|
+
//This is used for cases that requre to inject a default attachment handler
|
|
689
|
+
//TODO : this need to be removed from the conversation helper in the future since default attachment handler is a specific server feature
|
|
690
|
+
//reqConfig.headers['X-AGENT-HAS-ATTACHMENTS'] !== undefined;
|
|
691
|
+
reqConfig.headers['x-conversation-id'] !== undefined;
|
|
681
692
|
|
|
682
693
|
if (canRunLocally && !requiresRemoteCall) {
|
|
683
694
|
console.log('RUNNING AGENT LOCALLY');
|
|
@@ -783,19 +794,24 @@ export class Conversation extends EventEmitter {
|
|
|
783
794
|
const _arguments: any = {};
|
|
784
795
|
for (let arg of openApiArgs) {
|
|
785
796
|
_arguments[arg.name] = arg.schema;
|
|
786
|
-
if (tool.inputs
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
arg.schema.properties[prop].description
|
|
797
|
+
if (tool.inputs) {
|
|
798
|
+
if (arg.schema.properties) {
|
|
799
|
+
const required = [];
|
|
800
|
+
for (let prop in arg.schema.properties) {
|
|
801
|
+
const input = tool.inputs?.find((i) => i.name === prop);
|
|
802
|
+
if (!arg.schema.properties[prop].description) {
|
|
803
|
+
arg.schema.properties[prop].description = input?.description;
|
|
804
|
+
}
|
|
805
|
+
if (!input?.optional) {
|
|
806
|
+
required.push(prop);
|
|
807
|
+
}
|
|
792
808
|
}
|
|
793
|
-
if (
|
|
794
|
-
required
|
|
809
|
+
if (required.length) {
|
|
810
|
+
arg.schema.required = required;
|
|
795
811
|
}
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
arg.schema.
|
|
812
|
+
} else {
|
|
813
|
+
const input = tool.inputs?.find((i) => i.name === arg.name);
|
|
814
|
+
arg.schema.description = input?.description;
|
|
799
815
|
}
|
|
800
816
|
}
|
|
801
817
|
}
|
|
@@ -847,6 +863,23 @@ export class Conversation extends EventEmitter {
|
|
|
847
863
|
|
|
848
864
|
this._toolsConfig = toolsConfig;
|
|
849
865
|
}
|
|
866
|
+
|
|
867
|
+
async removeTool(toolName: string) {
|
|
868
|
+
this._customToolsDeclarations = this._customToolsDeclarations.filter((tool) => tool.name !== toolName);
|
|
869
|
+
delete this._customToolsHandlers[toolName];
|
|
870
|
+
const llmInference: LLMInference = await LLMInference.getInstance(this.model, AccessCandidate.team(this._teamId));
|
|
871
|
+
|
|
872
|
+
const toolsConfig: any = llmInference.connector.formatToolsConfig({
|
|
873
|
+
type: 'function',
|
|
874
|
+
toolDefinitions: this._customToolsDeclarations,
|
|
875
|
+
toolChoice: this.toolChoice,
|
|
876
|
+
});
|
|
877
|
+
this._toolsConfig = toolsConfig;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
public get toolNames() {
|
|
881
|
+
return this._customToolsDeclarations.map((tool) => tool.name);
|
|
882
|
+
}
|
|
850
883
|
/**
|
|
851
884
|
* updates LLM model, if spec is available, it will update the tools config
|
|
852
885
|
* @param model
|
|
@@ -928,6 +961,7 @@ export class Conversation extends EventEmitter {
|
|
|
928
961
|
//is this a valid agent data?
|
|
929
962
|
if (typeof specSource?.behavior === 'string' && specSource?.components && specSource?.connections) {
|
|
930
963
|
this.agentData = specSource; //agent loaded from data directly
|
|
964
|
+
this._specSource = specSource;
|
|
931
965
|
this._agentId = specSource.id;
|
|
932
966
|
return await this.loadSpecFromAgent(specSource);
|
|
933
967
|
}
|
|
@@ -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
|
+
}
|