@props-labs/mesh-os 0.1.7

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.
@@ -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;