@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.
Files changed (81) hide show
  1. package/CHANGELOG +15 -0
  2. package/dist/index.js +52 -46
  3. package/dist/index.js.map +1 -1
  4. package/dist/types/Components/APIEndpoint.class.d.ts +2 -8
  5. package/dist/types/Components/Component.class.d.ts +9 -0
  6. package/dist/types/Components/Triggers/Gmail.trigger.d.ts +0 -17
  7. package/dist/types/Components/Triggers/JobScheduler.trigger.d.ts +10 -0
  8. package/dist/types/Components/Triggers/Trigger.class.d.ts +11 -0
  9. package/dist/types/Components/index.d.ts +6 -0
  10. package/dist/types/Core/Connector.class.d.ts +1 -0
  11. package/dist/types/Core/ConnectorsService.d.ts +2 -0
  12. package/dist/types/Core/HookService.d.ts +1 -1
  13. package/dist/types/helpers/Conversation.helper.d.ts +2 -0
  14. package/dist/types/helpers/Crypto.helper.d.ts +8 -0
  15. package/dist/types/index.d.ts +13 -0
  16. package/dist/types/subsystems/AgentManager/Agent.class.d.ts +4 -2
  17. package/dist/types/subsystems/AgentManager/AgentData.service/AgentDataConnector.d.ts +13 -0
  18. package/dist/types/subsystems/AgentManager/AgentData.service/connectors/NullAgentData.class.d.ts +1 -4
  19. package/dist/types/subsystems/AgentManager/Scheduler.service/Job.class.d.ts +29 -6
  20. package/dist/types/subsystems/AgentManager/Scheduler.service/SchedulerConnector.d.ts +11 -3
  21. package/dist/types/subsystems/AgentManager/Scheduler.service/connectors/LocalScheduler.class.d.ts +31 -7
  22. package/dist/types/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.d.ts +2 -5
  23. package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.d.ts +3 -6
  24. package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.d.ts +7 -0
  25. package/dist/types/subsystems/LLMManager/LLM.service/connectors/xAI.class.d.ts +2 -5
  26. package/dist/types/types/Agent.types.d.ts +1 -0
  27. package/dist/types/types/LLM.types.d.ts +2 -5
  28. package/dist/types/types/SRE.types.d.ts +4 -1
  29. package/package.json +6 -2
  30. package/src/Components/APICall/OAuth.helper.ts +30 -35
  31. package/src/Components/APIEndpoint.class.ts +25 -6
  32. package/src/Components/Component.class.ts +11 -0
  33. package/src/Components/Triggers/Gmail.trigger.ts +282 -0
  34. package/src/Components/Triggers/JobScheduler.trigger.ts +45 -0
  35. package/src/Components/Triggers/README.md +3 -0
  36. package/src/Components/Triggers/Trigger.class.ts +101 -0
  37. package/src/Components/Triggers/WhatsApp.trigger.ts +219 -0
  38. package/src/Components/index.ts +8 -0
  39. package/src/Core/AgentProcess.helper.ts +4 -6
  40. package/src/Core/Connector.class.ts +11 -3
  41. package/src/Core/ConnectorsService.ts +5 -0
  42. package/src/Core/ExternalEventsReceiver.ts +317 -0
  43. package/src/Core/HookService.ts +20 -6
  44. package/src/Core/SmythRuntime.class.ts +7 -0
  45. package/src/Core/SystemEvents.ts +17 -0
  46. package/src/Core/boot.ts +2 -0
  47. package/src/helpers/Conversation.helper.ts +35 -11
  48. package/src/helpers/Crypto.helper.ts +28 -0
  49. package/src/index.ts +208 -195
  50. package/src/index.ts.bak +208 -195
  51. package/src/subsystems/AGENTS.md +594 -0
  52. package/src/subsystems/AgentManager/Agent.class.ts +71 -21
  53. package/src/subsystems/AgentManager/AgentData.service/AgentDataConnector.ts +24 -1
  54. package/src/subsystems/AgentManager/AgentData.service/connectors/NullAgentData.class.ts +2 -2
  55. package/src/subsystems/AgentManager/AgentRuntime.class.ts +34 -5
  56. package/src/subsystems/AgentManager/Scheduler.service/Job.class.ts +414 -0
  57. package/src/subsystems/AgentManager/Scheduler.service/Schedule.class.ts +200 -0
  58. package/src/subsystems/AgentManager/Scheduler.service/SchedulerConnector.ts +200 -0
  59. package/src/subsystems/AgentManager/Scheduler.service/connectors/LocalScheduler.class.ts +767 -0
  60. package/src/subsystems/AgentManager/Scheduler.service/index.ts +11 -0
  61. package/src/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.ts +1 -1
  62. package/src/subsystems/LLMManager/LLM.service/LLMCredentials.helper.ts +61 -2
  63. package/src/subsystems/LLMManager/LLM.service/connectors/Anthropic.class.ts +3 -0
  64. package/src/subsystems/LLMManager/LLM.service/connectors/Bedrock.class.ts +3 -1
  65. package/src/subsystems/LLMManager/LLM.service/connectors/Echo.class.ts +5 -1
  66. package/src/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.ts +247 -56
  67. package/src/subsystems/LLMManager/LLM.service/connectors/Groq.class.ts +3 -0
  68. package/src/subsystems/LLMManager/LLM.service/connectors/Ollama.class.ts +28 -21
  69. package/src/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.ts +3 -0
  70. package/src/subsystems/LLMManager/LLM.service/connectors/VertexAI.class.ts +121 -33
  71. package/src/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.ts +38 -27
  72. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts +115 -18
  73. package/src/subsystems/LLMManager/LLM.service/connectors/xAI.class.ts +3 -0
  74. package/src/subsystems/LLMManager/ModelsProvider.service/ModelsProviderConnector.ts +1 -6
  75. package/src/subsystems/MemoryManager/LLMContext.ts +3 -8
  76. package/src/subsystems/MemoryManager/RuntimeContext.ts +10 -9
  77. package/src/subsystems/Security/Credentials/Credentials.class.ts +1 -0
  78. package/src/subsystems/Security/Credentials/ManagedOAuth2Credentials.class.ts +106 -0
  79. package/src/types/Agent.types.ts +1 -0
  80. package/src/types/LLM.types.ts +2 -2
  81. 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
+ }
@@ -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().then(() => callback.apply(this, args))));
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(this, args))));
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().then(() => callback({ result, args, error })))
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(blockingAfterHooks[hookName].map((callback) => Promise.resolve(callback({ result, args, error }))));
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
@@ -67,6 +67,13 @@ export class SmythRuntime {
67
67
  Router: {
68
68
  Connector: 'NullRouter',
69
69
  },
70
+ Scheduler: {
71
+ Connector: 'LocalScheduler',
72
+ Settings: {
73
+ runJobs: true,
74
+ persistExecutionHistory: true,
75
+ },
76
+ },
70
77
  };
71
78
 
72
79
  protected constructor() {
@@ -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 && arg.schema.properties) {
787
- const required = [];
788
- for (let prop in arg.schema.properties) {
789
- const input = tool.inputs?.find((i) => i.name === prop);
790
- if (!arg.schema.properties[prop].description) {
791
- arg.schema.properties[prop].description = input?.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 (!input?.optional) {
794
- required.push(prop);
799
+ if (required.length) {
800
+ arg.schema.required = required;
795
801
  }
796
- }
797
- if (required.length) {
798
- arg.schema.required = required;
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
+ }