@props-labs/mesh-os 0.1.8 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -143,15 +143,8 @@ flowchart LR
143
143
 
144
144
  ## Getting Started
145
145
 
146
- ### Install & Create a New Instance
147
- ```bash
148
- # Install the CLI tool
149
- npm install -g @props-labs/mesh-os
146
+ > **Important Note**: The JavaScript client can only connect to an existing MeshOS instance. To launch a new MeshOS system, please use the [Python version](https://github.com/Props-Labs/mesh-os) which includes deployment capabilities.
150
147
 
151
- # Create and start a new project
152
- mesh-os init my-project && cd my-project
153
- mesh-os up
154
- ```
155
148
 
156
149
  ## Usage
157
150
  ```typescript
@@ -161,7 +154,7 @@ import dotenv from 'dotenv';
161
154
  // Load environment variables
162
155
  dotenv.config();
163
156
 
164
- // Initialize MeshOS
157
+ // Connect To MeshOS
165
158
  const client = new MeshOS({
166
159
  url: process.env.HASURA_URL || 'http://localhost:8080',
167
160
  apiKey: process.env.HASURA_ADMIN_SECRET || 'meshos',
@@ -91,7 +91,7 @@ export declare class MeshOS {
91
91
  /**
92
92
  * Store a new memory.
93
93
  */
94
- remember(content: string, agentId: string, metadata?: Partial<MemoryMetadata>): Promise<Memory>;
94
+ remember(content: string, agentId: string, metadata?: Partial<MemoryMetadata>): Promise<Memory | Memory[]>;
95
95
  /**
96
96
  * Delete a specific memory.
97
97
  */
@@ -107,7 +107,7 @@ export declare class MeshOS {
107
107
  /**
108
108
  * Update a memory and optionally create a version edge to the previous version.
109
109
  */
110
- updateMemory(memoryId: string, content: string, metadata?: Partial<MemoryMetadata>, createVersionEdge?: boolean): Promise<Memory>;
110
+ updateMemory(memoryId: string, content: string, metadata?: Partial<MemoryMetadata>, createVersionEdge?: boolean): Promise<Memory | Memory[]>;
111
111
  /**
112
112
  * Get memories connected to the given memory.
113
113
  */
@@ -12,7 +12,6 @@ const chalk_1 = __importDefault(require("chalk"));
12
12
  const boxen_1 = __importDefault(require("boxen"));
13
13
  const taxonomy_1 = require("./taxonomy");
14
14
  // Constants
15
- const MIN_THRESHOLD = 0.3;
16
15
  const SLUG_PATTERN = /^[a-z][a-z0-9_-]*[a-z0-9]$/;
17
16
  /**
18
17
  * GraphQL error class.
@@ -211,41 +210,120 @@ class MeshOS {
211
210
  additional: {},
212
211
  ...metadata
213
212
  });
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
213
+ // Check if content needs chunking (8192 tokens is approximately 32K characters)
214
+ const CHUNK_SIZE = 32000;
215
+ if (content.length <= CHUNK_SIZE) {
216
+ // Create embedding for single chunk
217
+ const embedding = await this.createEmbedding(content);
218
+ const embeddingStr = `[${embedding.join(',')}]`;
219
+ const query = `
220
+ mutation Remember($content: String!, $agentId: uuid!, $metadata: jsonb!, $embedding: vector!) {
221
+ insert_memories_one(object: {
222
+ content: $content,
223
+ agent_id: $agentId,
224
+ metadata: $metadata,
225
+ embedding: $embedding
226
+ }) {
227
+ id
228
+ agent_id
229
+ content
230
+ metadata
231
+ embedding
232
+ created_at
233
+ updated_at
234
+ }
232
235
  }
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
- };
236
+ `;
237
+ const result = await this.executeQuery(query, {
238
+ content,
239
+ agentId,
240
+ metadata: fullMetadata,
241
+ embedding: embeddingStr
242
+ });
243
+ // Convert snake_case to camelCase
244
+ const { agent_id, created_at, updated_at, ...rest } = result.insert_memories_one;
245
+ return {
246
+ ...rest,
247
+ agentId: agent_id,
248
+ createdAt: created_at,
249
+ updatedAt: updated_at,
250
+ };
251
+ }
252
+ // Split content into chunks
253
+ const chunks = [];
254
+ for (let i = 0; i < content.length; i += CHUNK_SIZE) {
255
+ chunks.push(content.slice(i, i + CHUNK_SIZE));
256
+ }
257
+ // Store each chunk and create links
258
+ const memories = [];
259
+ let previousChunkId = null;
260
+ for (let i = 0; i < chunks.length; i++) {
261
+ const chunkMetadata = {
262
+ ...fullMetadata,
263
+ additional: {
264
+ ...fullMetadata.additional,
265
+ chunk_info: {
266
+ chunk_index: i,
267
+ total_chunks: chunks.length,
268
+ is_chunk: true
269
+ }
270
+ }
271
+ };
272
+ // Create embedding for chunk
273
+ const embedding = await this.createEmbedding(chunks[i]);
274
+ const embeddingStr = `[${embedding.join(',')}]`;
275
+ const query = `
276
+ mutation Remember($content: String!, $agentId: uuid!, $metadata: jsonb!, $embedding: vector!) {
277
+ insert_memories_one(object: {
278
+ content: $content,
279
+ agent_id: $agentId,
280
+ metadata: $metadata,
281
+ embedding: $embedding
282
+ }) {
283
+ id
284
+ agent_id
285
+ content
286
+ metadata
287
+ embedding
288
+ created_at
289
+ updated_at
290
+ }
291
+ }
292
+ `;
293
+ const result = await this.executeQuery(query, {
294
+ content: chunks[i],
295
+ agentId,
296
+ metadata: chunkMetadata,
297
+ embedding: embeddingStr
298
+ });
299
+ // Convert snake_case to camelCase
300
+ const { agent_id, created_at, updated_at, ...rest } = result.insert_memories_one;
301
+ const memory = {
302
+ ...rest,
303
+ agentId: agent_id,
304
+ createdAt: created_at,
305
+ updatedAt: updated_at,
306
+ };
307
+ memories.push(memory);
308
+ // Link chunks sequentially
309
+ if (previousChunkId) {
310
+ await this.linkMemories(previousChunkId, memory.id, taxonomy_1.EdgeType.FOLLOWS_UP, 1.0, {
311
+ relationship: taxonomy_1.EdgeType.FOLLOWS_UP,
312
+ weight: 1.0,
313
+ bidirectional: false,
314
+ additional: { is_chunk_link: true }
315
+ });
316
+ // Also add PART_OF relationship to show these are parts of the same content
317
+ await this.linkMemories(memory.id, previousChunkId, taxonomy_1.EdgeType.PART_OF, 1.0, {
318
+ relationship: taxonomy_1.EdgeType.PART_OF,
319
+ weight: 1.0,
320
+ bidirectional: true,
321
+ additional: { is_chunk_link: true }
322
+ });
323
+ }
324
+ previousChunkId = memory.id;
325
+ }
326
+ return memories;
249
327
  }
250
328
  /**
251
329
  * Delete a specific memory.
@@ -364,7 +442,7 @@ class MeshOS {
364
442
  throw new Error(`Memory ${memoryId} not found`);
365
443
  }
366
444
  // Create new memory with updated content and metadata
367
- const newMemory = await this.remember(content, oldMemory.agent_id, {
445
+ const newMemories = await this.remember(content, oldMemory.agent_id, {
368
446
  ...oldMemory.metadata,
369
447
  version: oldMemory.metadata.version + 1,
370
448
  previousVersion: oldMemory.id,
@@ -372,14 +450,16 @@ class MeshOS {
372
450
  });
373
451
  // Create version edge if requested
374
452
  if (createVersionEdge) {
375
- await this.linkMemories(oldMemory.id, newMemory.id, taxonomy_1.EdgeType.VERSION_OF, 1.0, {
453
+ // If we got multiple memories (chunks), link the first one
454
+ const firstNewMemory = Array.isArray(newMemories) ? newMemories[0] : newMemories;
455
+ await this.linkMemories(oldMemory.id, firstNewMemory.id, taxonomy_1.EdgeType.VERSION_OF, 1.0, {
376
456
  relationship: taxonomy_1.EdgeType.VERSION_OF,
377
457
  weight: 1.0,
378
458
  bidirectional: false,
379
459
  additional: { version_increment: 1 }
380
460
  });
381
461
  }
382
- return newMemory;
462
+ return newMemories;
383
463
  }
384
464
  /**
385
465
  * Get memories connected to the given memory.
@@ -457,10 +537,11 @@ class MeshOS {
457
537
  if (results.length >= minResults) {
458
538
  return results.slice(0, limit);
459
539
  }
460
- // Second try: Adaptive threshold
540
+ // Second try: If adaptive threshold is enabled, try lowering the threshold
461
541
  if (adaptiveThreshold) {
462
542
  let currentThreshold = threshold - 0.05;
463
- while (currentThreshold >= MIN_THRESHOLD && results.length < minResults) {
543
+ const minThreshold = 0.3; // Don't go below this to avoid irrelevant matches
544
+ while (currentThreshold >= minThreshold && results.length < minResults) {
464
545
  const newResults = await this.recallWithThreshold({
465
546
  query,
466
547
  threshold: currentThreshold,
@@ -468,23 +549,25 @@ class MeshOS {
468
549
  limit,
469
550
  filters
470
551
  });
471
- // Add new unique results
552
+ // Add new results that aren't already in the list
472
553
  for (const result of newResults) {
473
554
  if (!results.some(r => r.id === result.id)) {
474
555
  results.push(result);
475
556
  }
476
557
  }
477
- if (results.length >= minResults)
558
+ if (results.length >= minResults) {
478
559
  break;
560
+ }
479
561
  currentThreshold -= 0.05;
480
562
  }
481
563
  }
482
- // Third try: Semantic expansion
564
+ // Third try: If we still don't have ANY results and semantic expansion is enabled
483
565
  if (results.length === 0 && useSemanticExpansion) {
484
566
  const variations = await this.expandQuery(query);
485
567
  const seenIds = new Map();
486
- // Try each variation with original threshold
487
- for (const variation of variations.slice(1)) {
568
+ const minThreshold = 0.3; // Don't go below this to avoid irrelevant matches
569
+ // Try each variation with the original threshold first
570
+ for (const variation of variations.slice(1)) { // Skip original query as we already tried it
488
571
  const variationResults = await this.recallWithThreshold({
489
572
  query: variation,
490
573
  threshold,
@@ -492,19 +575,21 @@ class MeshOS {
492
575
  limit,
493
576
  filters
494
577
  });
578
+ // Add new results or update if better similarity
495
579
  for (const memory of variationResults) {
496
580
  const existingMemory = seenIds.get(memory.id);
497
581
  if (!existingMemory || (memory.similarity || 0) > (existingMemory.similarity || 0)) {
498
582
  seenIds.set(memory.id, memory);
499
583
  }
500
584
  }
501
- if (seenIds.size >= minResults)
585
+ if (seenIds.size >= minResults) {
502
586
  break;
587
+ }
503
588
  }
504
589
  // If still no results, try variations with adaptive threshold
505
590
  if (seenIds.size === 0 && adaptiveThreshold) {
506
591
  let currentThreshold = threshold - 0.05;
507
- while (currentThreshold >= MIN_THRESHOLD && seenIds.size === 0) {
592
+ while (currentThreshold >= minThreshold && seenIds.size === 0) {
508
593
  for (const variation of variations.slice(1)) {
509
594
  const variationResults = await this.recallWithThreshold({
510
595
  query: variation,
@@ -519,19 +604,22 @@ class MeshOS {
519
604
  seenIds.set(memory.id, memory);
520
605
  }
521
606
  }
522
- if (seenIds.size > 0)
607
+ if (seenIds.size > 0) {
523
608
  break;
609
+ }
524
610
  }
525
- if (seenIds.size > 0)
611
+ if (seenIds.size > 0) {
526
612
  break;
613
+ }
527
614
  currentThreshold -= 0.05;
528
615
  }
529
616
  }
617
+ // Update results with any found memories
530
618
  if (seenIds.size > 0) {
531
619
  results = Array.from(seenIds.values());
532
620
  }
533
621
  }
534
- // Sort by similarity and return
622
+ // Sort by similarity and return top results
535
623
  results.sort((a, b) => (b.similarity || 0) - (a.similarity || 0));
536
624
  return results.slice(0, limit);
537
625
  }
@@ -557,13 +645,39 @@ class MeshOS {
557
645
  }
558
646
  }
559
647
  `;
648
+ // Process metadata filters
649
+ let metadataFilter;
650
+ if (filters) {
651
+ // Extract metadata filters
652
+ const { type, subtype, tags, relevance, version, _contains, ...additionalFilters } = filters;
653
+ metadataFilter = {};
654
+ // Add basic metadata filters
655
+ if (type)
656
+ metadataFilter.type = type;
657
+ if (subtype)
658
+ metadataFilter.subtype = subtype;
659
+ if (tags)
660
+ metadataFilter.tags = tags;
661
+ if (relevance)
662
+ metadataFilter.relevance = relevance;
663
+ if (version)
664
+ metadataFilter.version = version;
665
+ // Add array containment operations
666
+ if (_contains) {
667
+ metadataFilter._contains = _contains;
668
+ }
669
+ // Add any additional filters to the metadata.additional object
670
+ if (Object.keys(additionalFilters).length > 0) {
671
+ metadataFilter.additional = additionalFilters;
672
+ }
673
+ }
560
674
  const result = await this.executeQuery(gqlQuery, {
561
675
  args: {
562
676
  query_embedding: embeddingStr,
563
677
  match_threshold: threshold,
564
678
  match_count: limit,
565
679
  filter_agent_id: agentId,
566
- ...filters
680
+ metadata_filter: metadataFilter
567
681
  }
568
682
  });
569
683
  return result.search_memories.map(memory => ({
@@ -63,7 +63,8 @@ export declare enum EdgeType {
63
63
  CONTRADICTS = "contradicts",// Conflicting information
64
64
  DEPENDS_ON = "depends_on",// Prerequisite relationship
65
65
  SUMMARIZES = "summarizes",// Condensed version
66
- INFLUENCES = "influences"
66
+ INFLUENCES = "influences",// Impact relationship
67
+ PART_OF = "part_of"
67
68
  }
68
69
  /**
69
70
  * Relevance classification tags.
@@ -73,6 +73,7 @@ var EdgeType;
73
73
  EdgeType["DEPENDS_ON"] = "depends_on";
74
74
  EdgeType["SUMMARIZES"] = "summarizes";
75
75
  EdgeType["INFLUENCES"] = "influences";
76
+ EdgeType["PART_OF"] = "part_of";
76
77
  })(EdgeType || (exports.EdgeType = EdgeType = {}));
77
78
  /**
78
79
  * Relevance classification tags.
package/dist/index.d.ts CHANGED
@@ -2,5 +2,5 @@
2
2
  * MeshOS - A lightweight multi-agent memory system with semantic search.
3
3
  */
4
4
  export { Agent, Memory, MemoryEdge, MeshOS, GraphQLError, InvalidSlugError, type MeshOSConfig } from './core/client';
5
- export { DataType, ActivitySubtype, KnowledgeSubtype, DecisionSubtype, MediaSubtype, EdgeType, RelevanceTag, type VersionInfo, type MemoryMetadata, type EdgeMetadata, } from './core/taxonomy';
6
- export declare const VERSION = "0.1.6";
5
+ export { DataType, ActivitySubtype, KnowledgeSubtype, DecisionSubtype, MediaSubtype, EdgeType, RelevanceTag, CoreAgentStatus, type AgentStatus, type VersionInfo, type MemoryMetadata, type EdgeMetadata, } from './core/taxonomy';
6
+ export declare const VERSION = "0.1.9";
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * MeshOS - A lightweight multi-agent memory system with semantic search.
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.VERSION = exports.RelevanceTag = exports.EdgeType = exports.MediaSubtype = exports.DecisionSubtype = exports.KnowledgeSubtype = exports.ActivitySubtype = exports.DataType = exports.InvalidSlugError = exports.GraphQLError = exports.MeshOS = void 0;
6
+ exports.VERSION = exports.CoreAgentStatus = exports.RelevanceTag = exports.EdgeType = exports.MediaSubtype = exports.DecisionSubtype = exports.KnowledgeSubtype = exports.ActivitySubtype = exports.DataType = exports.InvalidSlugError = exports.GraphQLError = exports.MeshOS = void 0;
7
7
  var client_1 = require("./core/client");
8
8
  Object.defineProperty(exports, "MeshOS", { enumerable: true, get: function () { return client_1.MeshOS; } });
9
9
  Object.defineProperty(exports, "GraphQLError", { enumerable: true, get: function () { return client_1.GraphQLError; } });
@@ -17,4 +17,6 @@ Object.defineProperty(exports, "DecisionSubtype", { enumerable: true, get: funct
17
17
  Object.defineProperty(exports, "MediaSubtype", { enumerable: true, get: function () { return taxonomy_1.MediaSubtype; } });
18
18
  Object.defineProperty(exports, "EdgeType", { enumerable: true, get: function () { return taxonomy_1.EdgeType; } });
19
19
  Object.defineProperty(exports, "RelevanceTag", { enumerable: true, get: function () { return taxonomy_1.RelevanceTag; } });
20
- exports.VERSION = '0.1.6';
20
+ // Agent status types
21
+ Object.defineProperty(exports, "CoreAgentStatus", { enumerable: true, get: function () { return taxonomy_1.CoreAgentStatus; } });
22
+ exports.VERSION = '0.1.9';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@props-labs/mesh-os",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "MeshOS - A memory system for AI agents",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,4 +14,10 @@ computed_fields:
14
14
  function:
15
15
  name: search_memories
16
16
  schema: public
17
+ arguments:
18
+ query_embedding: $query_embedding
19
+ match_threshold: $match_threshold
20
+ match_count: $match_count
21
+ filter_agent_id: $filter_agent_id
22
+ metadata_filter: $metadata_filter
17
23
  comment: Computes similarity score for vector search
@@ -1,13 +1,26 @@
1
- -- Drop functions
2
- DROP FUNCTION IF EXISTS search_memories;
1
+ -- Drop all versions of search_memories function
2
+ DROP FUNCTION IF EXISTS public.search_memories(vector(1536), float8, integer, uuid, jsonb);
3
+ DROP FUNCTION IF EXISTS public.search_memories(vector(1536), float8, integer, uuid);
4
+ DROP FUNCTION IF EXISTS public.search_memories;
5
+
6
+ -- Drop other functions
3
7
  DROP FUNCTION IF EXISTS get_connected_memories;
8
+
9
+ -- Drop triggers first
10
+ DROP TRIGGER IF EXISTS update_agents_updated_at ON public.agents;
11
+ DROP TRIGGER IF EXISTS update_memories_updated_at ON public.memories;
12
+
13
+ -- Now we can safely drop the trigger function
4
14
  DROP FUNCTION IF EXISTS update_updated_at_column;
5
15
 
16
+ -- Drop views first
17
+ DROP VIEW IF EXISTS public.memories_with_similarity;
18
+
6
19
  -- Drop tables
7
20
  DROP TABLE IF EXISTS public.memory_edges;
8
21
  DROP TABLE IF EXISTS public.memories;
9
22
  DROP TABLE IF EXISTS public.agents;
10
23
 
11
- -- Drop extensions
12
- DROP EXTENSION IF EXISTS vector;
24
+ -- Drop extensions (with CASCADE for vector since it has dependent objects)
25
+ DROP EXTENSION IF EXISTS vector CASCADE;
13
26
  DROP EXTENSION IF EXISTS "uuid-ossp";
@@ -0,0 +1,4 @@
1
+ -- Drop the updated search_memories function with metadata filtering
2
+ DROP FUNCTION public.search_memories(vector(1536), float8, integer, uuid, jsonb);
3
+
4
+ -- Note: The original search_memories function from 1_init will be restored when that migration is reapplied
@@ -0,0 +1,44 @@
1
+ -- Drop the existing search_memories function
2
+ DROP FUNCTION IF EXISTS public.search_memories;
3
+
4
+ -- Create the updated search_memories function with metadata filtering
5
+ CREATE OR REPLACE FUNCTION public.search_memories(
6
+ query_embedding vector(1536),
7
+ match_threshold float8,
8
+ match_count integer,
9
+ filter_agent_id uuid DEFAULT NULL,
10
+ metadata_filter jsonb DEFAULT NULL
11
+ )
12
+ RETURNS SETOF public.memories_with_similarity
13
+ LANGUAGE sql
14
+ STABLE
15
+ AS $$
16
+ WITH normalized_query AS (
17
+ SELECT l2_normalize(query_embedding) AS normalized_vector
18
+ )
19
+ SELECT
20
+ m.id,
21
+ m.agent_id,
22
+ m.content,
23
+ m.metadata,
24
+ m.embedding,
25
+ m.created_at,
26
+ m.updated_at,
27
+ -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) as similarity
28
+ FROM memories m
29
+ WHERE
30
+ CASE
31
+ WHEN filter_agent_id IS NOT NULL THEN m.agent_id = filter_agent_id
32
+ ELSE TRUE
33
+ END
34
+ AND CASE
35
+ WHEN metadata_filter IS NOT NULL THEN m.metadata @> metadata_filter
36
+ ELSE TRUE
37
+ END
38
+ AND -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) >= match_threshold
39
+ ORDER BY -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) DESC
40
+ LIMIT match_count;
41
+ $$;
42
+
43
+ -- Track the function in Hasura
44
+ COMMENT ON FUNCTION public.search_memories IS E'@graphql({"type": "Query"})';