@props-labs/mesh-os 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,581 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MeshOS = exports.InvalidSlugError = exports.GraphQLError = void 0;
7
+ /**
8
+ * Core functionality for MeshOS.
9
+ */
10
+ const openai_1 = __importDefault(require("openai"));
11
+ const chalk_1 = __importDefault(require("chalk"));
12
+ const boxen_1 = __importDefault(require("boxen"));
13
+ const taxonomy_1 = require("./taxonomy");
14
+ // Constants
15
+ const MIN_THRESHOLD = 0.3;
16
+ const SLUG_PATTERN = /^[a-z][a-z0-9_-]*[a-z0-9]$/;
17
+ /**
18
+ * GraphQL error class.
19
+ */
20
+ class GraphQLError extends Error {
21
+ constructor(message) {
22
+ super(message);
23
+ this.name = 'GraphQLError';
24
+ }
25
+ }
26
+ exports.GraphQLError = GraphQLError;
27
+ /**
28
+ * Invalid slug error class.
29
+ */
30
+ class InvalidSlugError extends Error {
31
+ constructor(message) {
32
+ super(message);
33
+ this.name = 'InvalidSlugError';
34
+ }
35
+ }
36
+ exports.InvalidSlugError = InvalidSlugError;
37
+ /**
38
+ * MeshOS client for interacting with the system.
39
+ */
40
+ class MeshOS {
41
+ constructor(config = {}) {
42
+ this.url = `${config.url || 'http://localhost:8080'}/v1/graphql`;
43
+ this.headers = {
44
+ 'Content-Type': 'application/json',
45
+ 'x-hasura-admin-secret': config.apiKey || 'meshos'
46
+ };
47
+ // Set up OpenAI
48
+ const openaiApiKey = config.openaiApiKey || process.env.OPENAI_API_KEY;
49
+ if (!openaiApiKey) {
50
+ const boxOptions = {
51
+ title: 'Missing API Key',
52
+ titleAlignment: 'center',
53
+ padding: 1,
54
+ borderColor: 'yellow',
55
+ borderStyle: 'round'
56
+ };
57
+ console.error((0, boxen_1.default)(chalk_1.default.yellow('⚠️ OpenAI API key not found!\n\n') +
58
+ 'Please set your OpenAI API key in the environment:\n' +
59
+ chalk_1.default.green('OPENAI_API_KEY=your-key-here') + '\n\n' +
60
+ 'You can get an API key at: ' + chalk_1.default.blue('https://platform.openai.com/api-keys'), boxOptions));
61
+ throw new Error('OpenAI API key is required');
62
+ }
63
+ this.openai = new openai_1.default({ apiKey: openaiApiKey });
64
+ }
65
+ validateSlug(slug) {
66
+ return SLUG_PATTERN.test(slug);
67
+ }
68
+ /**
69
+ * Execute a GraphQL query.
70
+ */
71
+ async executeQuery(query, variables) {
72
+ const response = await fetch(this.url, {
73
+ method: 'POST',
74
+ headers: this.headers,
75
+ body: JSON.stringify({
76
+ query,
77
+ variables: variables || {}
78
+ })
79
+ });
80
+ if (!response.ok) {
81
+ throw new Error(`HTTP error! status: ${response.status}`);
82
+ }
83
+ const result = await response.json();
84
+ if (result.errors) {
85
+ throw new GraphQLError(result.errors[0].message);
86
+ }
87
+ return result.data;
88
+ }
89
+ /**
90
+ * Create an embedding for the given text.
91
+ */
92
+ async createEmbedding(text) {
93
+ const response = await this.openai.embeddings.create({
94
+ model: 'text-embedding-3-small',
95
+ input: text
96
+ });
97
+ return response.data[0].embedding;
98
+ }
99
+ /**
100
+ * Register a new agent in the system.
101
+ */
102
+ async registerAgent(name, description, metadata, slug) {
103
+ if (slug && !this.validateSlug(slug)) {
104
+ throw new InvalidSlugError("Slug must start with a letter and contain only lowercase letters, " +
105
+ "numbers, hyphens, and underscores");
106
+ }
107
+ // First check if agent with slug exists
108
+ if (slug) {
109
+ const existing = await this.getAgentBySlug(slug);
110
+ if (existing) {
111
+ return existing;
112
+ }
113
+ }
114
+ const query = `
115
+ mutation RegisterAgent($name: String!, $description: String!, $metadata: jsonb, $slug: String) {
116
+ insert_agents_one(object: {
117
+ name: $name,
118
+ description: $description,
119
+ metadata: $metadata,
120
+ status: "active",
121
+ slug: $slug
122
+ }) {
123
+ id
124
+ name
125
+ description
126
+ metadata
127
+ status
128
+ slug
129
+ }
130
+ }
131
+ `;
132
+ const result = await this.executeQuery(query, {
133
+ name,
134
+ description,
135
+ metadata: metadata || {},
136
+ slug
137
+ });
138
+ return result.insert_agents_one;
139
+ }
140
+ /**
141
+ * Get agent details by slug.
142
+ */
143
+ async getAgentBySlug(slug) {
144
+ if (!this.validateSlug(slug)) {
145
+ throw new InvalidSlugError("Slug must start with a letter and contain only lowercase letters, " +
146
+ "numbers, hyphens, and underscores");
147
+ }
148
+ const query = `
149
+ query GetAgentBySlug($slug: String!) {
150
+ agents(where: {slug: {_eq: $slug}}, limit: 1) {
151
+ id
152
+ name
153
+ description
154
+ metadata
155
+ status
156
+ slug
157
+ }
158
+ }
159
+ `;
160
+ const result = await this.executeQuery(query, { slug });
161
+ return result.agents[0] || null;
162
+ }
163
+ /**
164
+ * Unregister an agent and remove all their memories.
165
+ */
166
+ async unregisterAgent(agentId) {
167
+ const query = `
168
+ mutation UnregisterAgent($id: uuid!) {
169
+ delete_agents_by_pk(id: $id) {
170
+ id
171
+ }
172
+ }
173
+ `;
174
+ const result = await this.executeQuery(query, {
175
+ id: agentId
176
+ });
177
+ return result.delete_agents_by_pk !== null;
178
+ }
179
+ /**
180
+ * Get agent details by ID.
181
+ */
182
+ async getAgent(agentId) {
183
+ const query = `
184
+ query GetAgent($id: uuid!) {
185
+ agents_by_pk(id: $id) {
186
+ id
187
+ name
188
+ description
189
+ metadata
190
+ status
191
+ slug
192
+ }
193
+ }
194
+ `;
195
+ const result = await this.executeQuery(query, {
196
+ id: agentId
197
+ });
198
+ return result.agents_by_pk;
199
+ }
200
+ /**
201
+ * Store a new memory.
202
+ */
203
+ async remember(content, agentId, metadata) {
204
+ // Create default metadata if none provided
205
+ const fullMetadata = taxonomy_1.memoryMetadataSchema.parse({
206
+ type: taxonomy_1.DataType.KNOWLEDGE,
207
+ subtype: taxonomy_1.KnowledgeSubtype.DATASET,
208
+ tags: [],
209
+ version: 1,
210
+ history: [],
211
+ additional: {},
212
+ ...metadata
213
+ });
214
+ // Create embedding
215
+ const embedding = await this.createEmbedding(content);
216
+ const embeddingStr = `[${embedding.join(',')}]`;
217
+ const query = `
218
+ mutation Remember($content: String!, $agentId: uuid!, $metadata: jsonb!, $embedding: vector!) {
219
+ insert_memories_one(object: {
220
+ content: $content,
221
+ agent_id: $agentId,
222
+ metadata: $metadata,
223
+ embedding: $embedding
224
+ }) {
225
+ id
226
+ agent_id
227
+ content
228
+ metadata
229
+ embedding
230
+ created_at
231
+ updated_at
232
+ }
233
+ }
234
+ `;
235
+ const result = await this.executeQuery(query, {
236
+ content,
237
+ agentId,
238
+ metadata: fullMetadata,
239
+ embedding: embeddingStr
240
+ });
241
+ // Convert snake_case to camelCase
242
+ const { agent_id, created_at, updated_at, ...rest } = result.insert_memories_one;
243
+ return {
244
+ ...rest,
245
+ agentId: agent_id,
246
+ createdAt: created_at,
247
+ updatedAt: updated_at,
248
+ };
249
+ }
250
+ /**
251
+ * Delete a specific memory.
252
+ */
253
+ async forget(memoryId) {
254
+ const query = `
255
+ mutation Forget($id: uuid!) {
256
+ delete_memories_by_pk(id: $id) {
257
+ id
258
+ }
259
+ }
260
+ `;
261
+ const result = await this.executeQuery(query, {
262
+ id: memoryId
263
+ });
264
+ return result.delete_memories_by_pk !== null;
265
+ }
266
+ /**
267
+ * Create a link between two memories.
268
+ */
269
+ async linkMemories(sourceMemoryId, targetMemoryId, relationship, weight = 1.0, metadata) {
270
+ const query = `
271
+ mutation LinkMemories(
272
+ $sourceMemory: uuid!,
273
+ $targetMemory: uuid!,
274
+ $relationship: String!,
275
+ $weight: float8!,
276
+ $metadata: jsonb!
277
+ ) {
278
+ insert_memory_edges_one(object: {
279
+ source_memory: $sourceMemory,
280
+ target_memory: $targetMemory,
281
+ relationship: $relationship,
282
+ weight: $weight,
283
+ metadata: $metadata
284
+ }) {
285
+ id
286
+ source_memory
287
+ target_memory
288
+ relationship
289
+ weight
290
+ created_at
291
+ metadata
292
+ }
293
+ }
294
+ `;
295
+ const fullMetadata = {
296
+ relationship,
297
+ weight,
298
+ bidirectional: false,
299
+ additional: {},
300
+ ...metadata
301
+ };
302
+ const result = await this.executeQuery(query, {
303
+ sourceMemory: sourceMemoryId,
304
+ targetMemory: targetMemoryId,
305
+ relationship,
306
+ weight,
307
+ metadata: fullMetadata
308
+ });
309
+ // Convert snake_case to camelCase
310
+ const { source_memory, target_memory, created_at, ...rest } = result.insert_memory_edges_one;
311
+ return {
312
+ ...rest,
313
+ sourceMemory: source_memory,
314
+ targetMemory: target_memory,
315
+ createdAt: created_at,
316
+ };
317
+ }
318
+ /**
319
+ * Remove links between two memories.
320
+ */
321
+ async unlinkMemories(sourceMemoryId, targetMemoryId, relationship) {
322
+ const conditions = {
323
+ source_memory: { _eq: sourceMemoryId },
324
+ target_memory: { _eq: targetMemoryId }
325
+ };
326
+ if (relationship) {
327
+ conditions.relationship = { _eq: relationship };
328
+ }
329
+ const query = `
330
+ mutation UnlinkMemories($where: memory_edges_bool_exp!) {
331
+ delete_memory_edges(where: $where) {
332
+ affected_rows
333
+ }
334
+ }
335
+ `;
336
+ const result = await this.executeQuery(query, {
337
+ where: conditions
338
+ });
339
+ return result.delete_memory_edges.affected_rows > 0;
340
+ }
341
+ /**
342
+ * Update a memory and optionally create a version edge to the previous version.
343
+ */
344
+ async updateMemory(memoryId, content, metadata, createVersionEdge = true) {
345
+ // First get the current memory
346
+ const query = `
347
+ query GetMemory($id: uuid!) {
348
+ memories_by_pk(id: $id) {
349
+ id
350
+ agent_id
351
+ content
352
+ metadata
353
+ embedding
354
+ created_at
355
+ updated_at
356
+ }
357
+ }
358
+ `;
359
+ const result = await this.executeQuery(query, {
360
+ id: memoryId
361
+ });
362
+ const oldMemory = result.memories_by_pk;
363
+ if (!oldMemory) {
364
+ throw new Error(`Memory ${memoryId} not found`);
365
+ }
366
+ // Create new memory with updated content and metadata
367
+ const newMemory = await this.remember(content, oldMemory.agent_id, {
368
+ ...oldMemory.metadata,
369
+ version: oldMemory.metadata.version + 1,
370
+ previousVersion: oldMemory.id,
371
+ ...metadata
372
+ });
373
+ // Create version edge if requested
374
+ if (createVersionEdge) {
375
+ await this.linkMemories(oldMemory.id, newMemory.id, taxonomy_1.EdgeType.VERSION_OF, 1.0, {
376
+ relationship: taxonomy_1.EdgeType.VERSION_OF,
377
+ weight: 1.0,
378
+ bidirectional: false,
379
+ additional: { version_increment: 1 }
380
+ });
381
+ }
382
+ return newMemory;
383
+ }
384
+ /**
385
+ * Get memories connected to the given memory.
386
+ */
387
+ async getConnectedMemories(memoryId, relationship, maxDepth = 1) {
388
+ const query = `
389
+ query GetConnectedMemories(
390
+ $memoryId: uuid!,
391
+ $relationship: String,
392
+ $maxDepth: Int!
393
+ ) {
394
+ get_connected_memories(
395
+ memory_id: $memoryId,
396
+ relationship_type: $relationship,
397
+ max_depth: $maxDepth
398
+ ) {
399
+ source_id
400
+ target_id
401
+ relationship
402
+ weight
403
+ depth
404
+ }
405
+ }
406
+ `;
407
+ const result = await this.executeQuery(query, {
408
+ memoryId,
409
+ relationship,
410
+ maxDepth
411
+ });
412
+ return result.get_connected_memories;
413
+ }
414
+ /**
415
+ * Generate semantic variations of the query.
416
+ */
417
+ async expandQuery(query, numVariations = 2) {
418
+ const systemPrompt = `Generate ${numVariations} semantic variations of the query that mean the same thing.\n` +
419
+ 'Focus on different ways to express the same concept.\n' +
420
+ 'Return ONLY the variations, one per line, no numbering or extra text.\n' +
421
+ 'Variations should be concise and natural, similar in length to the original.';
422
+ const response = await this.openai.chat.completions.create({
423
+ model: 'gpt-3.5-turbo',
424
+ messages: [{
425
+ role: 'system',
426
+ content: systemPrompt
427
+ }, {
428
+ role: 'user',
429
+ content: query
430
+ }],
431
+ temperature: 0.7
432
+ });
433
+ const content = response.choices[0]?.message?.content;
434
+ if (!content) {
435
+ return [query];
436
+ }
437
+ const result = [query]; // Include original
438
+ result.push(...content
439
+ .split('\n')
440
+ .map(v => v.trim())
441
+ .filter(Boolean));
442
+ return result.slice(0, numVariations + 1);
443
+ }
444
+ /**
445
+ * Search memories by semantic similarity.
446
+ */
447
+ async recall(options) {
448
+ const { query, agentId, limit = 5, threshold = 0.7, minResults = 1, adaptiveThreshold = true, useSemanticExpansion = true, filters } = options;
449
+ // First try: Direct search with initial threshold
450
+ let results = await this.recallWithThreshold({
451
+ query,
452
+ threshold,
453
+ agentId,
454
+ limit,
455
+ filters
456
+ });
457
+ if (results.length >= minResults) {
458
+ return results.slice(0, limit);
459
+ }
460
+ // Second try: Adaptive threshold
461
+ if (adaptiveThreshold) {
462
+ let currentThreshold = threshold - 0.05;
463
+ while (currentThreshold >= MIN_THRESHOLD && results.length < minResults) {
464
+ const newResults = await this.recallWithThreshold({
465
+ query,
466
+ threshold: currentThreshold,
467
+ agentId,
468
+ limit,
469
+ filters
470
+ });
471
+ // Add new unique results
472
+ for (const result of newResults) {
473
+ if (!results.some(r => r.id === result.id)) {
474
+ results.push(result);
475
+ }
476
+ }
477
+ if (results.length >= minResults)
478
+ break;
479
+ currentThreshold -= 0.05;
480
+ }
481
+ }
482
+ // Third try: Semantic expansion
483
+ if (results.length === 0 && useSemanticExpansion) {
484
+ const variations = await this.expandQuery(query);
485
+ const seenIds = new Map();
486
+ // Try each variation with original threshold
487
+ for (const variation of variations.slice(1)) {
488
+ const variationResults = await this.recallWithThreshold({
489
+ query: variation,
490
+ threshold,
491
+ agentId,
492
+ limit,
493
+ filters
494
+ });
495
+ for (const memory of variationResults) {
496
+ const existingMemory = seenIds.get(memory.id);
497
+ if (!existingMemory || (memory.similarity || 0) > (existingMemory.similarity || 0)) {
498
+ seenIds.set(memory.id, memory);
499
+ }
500
+ }
501
+ if (seenIds.size >= minResults)
502
+ break;
503
+ }
504
+ // If still no results, try variations with adaptive threshold
505
+ if (seenIds.size === 0 && adaptiveThreshold) {
506
+ let currentThreshold = threshold - 0.05;
507
+ while (currentThreshold >= MIN_THRESHOLD && seenIds.size === 0) {
508
+ for (const variation of variations.slice(1)) {
509
+ const variationResults = await this.recallWithThreshold({
510
+ query: variation,
511
+ threshold: currentThreshold,
512
+ agentId,
513
+ limit,
514
+ filters
515
+ });
516
+ for (const memory of variationResults) {
517
+ const existingMemory = seenIds.get(memory.id);
518
+ if (!existingMemory || (memory.similarity || 0) > (existingMemory.similarity || 0)) {
519
+ seenIds.set(memory.id, memory);
520
+ }
521
+ }
522
+ if (seenIds.size > 0)
523
+ break;
524
+ }
525
+ if (seenIds.size > 0)
526
+ break;
527
+ currentThreshold -= 0.05;
528
+ }
529
+ }
530
+ if (seenIds.size > 0) {
531
+ results = Array.from(seenIds.values());
532
+ }
533
+ }
534
+ // Sort by similarity and return
535
+ results.sort((a, b) => (b.similarity || 0) - (a.similarity || 0));
536
+ return results.slice(0, limit);
537
+ }
538
+ /**
539
+ * Internal method to perform recall with a specific threshold.
540
+ */
541
+ async recallWithThreshold(options) {
542
+ const { query, threshold, agentId, limit = 10, filters } = options;
543
+ // Create embedding for the query
544
+ const embedding = await this.createEmbedding(query);
545
+ const embeddingStr = `[${embedding.join(',')}]`;
546
+ const gqlQuery = `
547
+ query SearchMemories($args: search_memories_args!) {
548
+ search_memories(args: $args) {
549
+ id
550
+ agent_id
551
+ content
552
+ metadata
553
+ embedding
554
+ similarity
555
+ created_at
556
+ updated_at
557
+ }
558
+ }
559
+ `;
560
+ const result = await this.executeQuery(gqlQuery, {
561
+ args: {
562
+ query_embedding: embeddingStr,
563
+ match_threshold: threshold,
564
+ match_count: limit,
565
+ filter_agent_id: agentId,
566
+ ...filters
567
+ }
568
+ });
569
+ return result.search_memories.map(memory => ({
570
+ id: memory.id,
571
+ agentId: memory.agent_id,
572
+ content: memory.content,
573
+ metadata: memory.metadata,
574
+ embedding: memory.embedding,
575
+ similarity: memory.similarity,
576
+ createdAt: memory.created_at,
577
+ updatedAt: memory.updated_at
578
+ }));
579
+ }
580
+ }
581
+ exports.MeshOS = MeshOS;