@props-labs/mesh-os 0.1.9 → 0.1.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- import { EdgeType, type AgentStatus, type EdgeMetadata, type MemoryMetadata } from './taxonomy';
1
+ import { EdgeType, type AgentStatus, type EdgeMetadata, type MemoryMetadata, type TimestampFilter } from './taxonomy';
2
2
  /**
3
3
  * An agent in the system.
4
4
  */
@@ -21,6 +21,7 @@ export interface Memory {
21
21
  embedding: number[];
22
22
  createdAt: string;
23
23
  updatedAt: string;
24
+ expiresAt?: string;
24
25
  similarity?: number;
25
26
  }
26
27
  /**
@@ -91,7 +92,7 @@ export declare class MeshOS {
91
92
  /**
92
93
  * Store a new memory.
93
94
  */
94
- remember(content: string, agentId: string, metadata?: Partial<MemoryMetadata>): Promise<Memory | Memory[]>;
95
+ remember(content: string, agentId: string, metadata?: Partial<MemoryMetadata>, expiresAt?: string): Promise<Memory | Memory[]>;
95
96
  /**
96
97
  * Delete a specific memory.
97
98
  */
@@ -134,6 +135,8 @@ export declare class MeshOS {
134
135
  adaptiveThreshold?: boolean;
135
136
  useSemanticExpansion?: boolean;
136
137
  filters?: Record<string, unknown>;
138
+ createdAtFilter?: TimestampFilter;
139
+ expiresAtFilter?: TimestampFilter;
137
140
  }): Promise<Memory[]>;
138
141
  /**
139
142
  * Internal method to perform recall with a specific threshold.
@@ -143,4 +146,18 @@ export declare class MeshOS {
143
146
  * Update an agent's status.
144
147
  */
145
148
  updateAgentStatus(agentId: string, status: AgentStatus): Promise<Agent>;
149
+ /**
150
+ * Query memories with flexible filtering, sorting, and pagination.
151
+ */
152
+ getMemories(options: {
153
+ where?: Record<string, unknown>;
154
+ orderBy?: Array<{
155
+ column: string;
156
+ order: 'asc' | 'desc';
157
+ }>;
158
+ limit?: number;
159
+ offset?: number;
160
+ distinct?: boolean;
161
+ distinctOn?: string[];
162
+ }): Promise<Memory[]>;
146
163
  }
@@ -199,7 +199,7 @@ class MeshOS {
199
199
  /**
200
200
  * Store a new memory.
201
201
  */
202
- async remember(content, agentId, metadata) {
202
+ async remember(content, agentId, metadata, expiresAt) {
203
203
  // Create default metadata if none provided
204
204
  const fullMetadata = taxonomy_1.memoryMetadataSchema.parse({
205
205
  type: taxonomy_1.DataType.KNOWLEDGE,
@@ -217,12 +217,13 @@ class MeshOS {
217
217
  const embedding = await this.createEmbedding(content);
218
218
  const embeddingStr = `[${embedding.join(',')}]`;
219
219
  const query = `
220
- mutation Remember($content: String!, $agentId: uuid!, $metadata: jsonb!, $embedding: vector!) {
220
+ mutation Remember($content: String!, $agentId: uuid!, $metadata: jsonb!, $embedding: vector!, $expiresAt: timestamptz) {
221
221
  insert_memories_one(object: {
222
222
  content: $content,
223
223
  agent_id: $agentId,
224
224
  metadata: $metadata,
225
- embedding: $embedding
225
+ embedding: $embedding,
226
+ expires_at: $expiresAt
226
227
  }) {
227
228
  id
228
229
  agent_id
@@ -231,6 +232,7 @@ class MeshOS {
231
232
  embedding
232
233
  created_at
233
234
  updated_at
235
+ expires_at
234
236
  }
235
237
  }
236
238
  `;
@@ -238,15 +240,17 @@ class MeshOS {
238
240
  content,
239
241
  agentId,
240
242
  metadata: fullMetadata,
241
- embedding: embeddingStr
243
+ embedding: embeddingStr,
244
+ expiresAt
242
245
  });
243
246
  // Convert snake_case to camelCase
244
- const { agent_id, created_at, updated_at, ...rest } = result.insert_memories_one;
247
+ const { agent_id, created_at, updated_at, expires_at, ...rest } = result.insert_memories_one;
245
248
  return {
246
249
  ...rest,
247
250
  agentId: agent_id,
248
251
  createdAt: created_at,
249
252
  updatedAt: updated_at,
253
+ expiresAt: expires_at,
250
254
  };
251
255
  }
252
256
  // Split content into chunks
@@ -273,12 +277,13 @@ class MeshOS {
273
277
  const embedding = await this.createEmbedding(chunks[i]);
274
278
  const embeddingStr = `[${embedding.join(',')}]`;
275
279
  const query = `
276
- mutation Remember($content: String!, $agentId: uuid!, $metadata: jsonb!, $embedding: vector!) {
280
+ mutation Remember($content: String!, $agentId: uuid!, $metadata: jsonb!, $embedding: vector!, $expiresAt: timestamptz) {
277
281
  insert_memories_one(object: {
278
282
  content: $content,
279
283
  agent_id: $agentId,
280
284
  metadata: $metadata,
281
- embedding: $embedding
285
+ embedding: $embedding,
286
+ expires_at: $expiresAt
282
287
  }) {
283
288
  id
284
289
  agent_id
@@ -287,6 +292,7 @@ class MeshOS {
287
292
  embedding
288
293
  created_at
289
294
  updated_at
295
+ expires_at
290
296
  }
291
297
  }
292
298
  `;
@@ -294,15 +300,17 @@ class MeshOS {
294
300
  content: chunks[i],
295
301
  agentId,
296
302
  metadata: chunkMetadata,
297
- embedding: embeddingStr
303
+ embedding: embeddingStr,
304
+ expiresAt
298
305
  });
299
306
  // Convert snake_case to camelCase
300
- const { agent_id, created_at, updated_at, ...rest } = result.insert_memories_one;
307
+ const { agent_id, created_at, updated_at, expires_at, ...rest } = result.insert_memories_one;
301
308
  const memory = {
302
309
  ...rest,
303
310
  agentId: agent_id,
304
311
  createdAt: created_at,
305
312
  updatedAt: updated_at,
313
+ expiresAt: expires_at,
306
314
  };
307
315
  memories.push(memory);
308
316
  // Link chunks sequentially
@@ -525,14 +533,16 @@ class MeshOS {
525
533
  * Search memories by semantic similarity.
526
534
  */
527
535
  async recall(options) {
528
- const { query, agentId, limit = 5, threshold = 0.7, minResults = 1, adaptiveThreshold = true, useSemanticExpansion = true, filters } = options;
536
+ const { query, agentId, limit = 5, threshold = 0.7, minResults = 1, adaptiveThreshold = true, useSemanticExpansion = true, filters, createdAtFilter, expiresAtFilter } = options;
529
537
  // First try: Direct search with initial threshold
530
538
  let results = await this.recallWithThreshold({
531
539
  query,
532
540
  threshold,
533
541
  agentId,
534
542
  limit,
535
- filters
543
+ filters,
544
+ createdAtFilter,
545
+ expiresAtFilter
536
546
  });
537
547
  if (results.length >= minResults) {
538
548
  return results.slice(0, limit);
@@ -547,7 +557,9 @@ class MeshOS {
547
557
  threshold: currentThreshold,
548
558
  agentId,
549
559
  limit,
550
- filters
560
+ filters,
561
+ createdAtFilter,
562
+ expiresAtFilter
551
563
  });
552
564
  // Add new results that aren't already in the list
553
565
  for (const result of newResults) {
@@ -573,7 +585,9 @@ class MeshOS {
573
585
  threshold,
574
586
  agentId,
575
587
  limit,
576
- filters
588
+ filters,
589
+ createdAtFilter,
590
+ expiresAtFilter
577
591
  });
578
592
  // Add new results or update if better similarity
579
593
  for (const memory of variationResults) {
@@ -596,7 +610,9 @@ class MeshOS {
596
610
  threshold: currentThreshold,
597
611
  agentId,
598
612
  limit,
599
- filters
613
+ filters,
614
+ createdAtFilter,
615
+ expiresAtFilter
600
616
  });
601
617
  for (const memory of variationResults) {
602
618
  const existingMemory = seenIds.get(memory.id);
@@ -627,7 +643,7 @@ class MeshOS {
627
643
  * Internal method to perform recall with a specific threshold.
628
644
  */
629
645
  async recallWithThreshold(options) {
630
- const { query, threshold, agentId, limit = 10, filters } = options;
646
+ const { query, threshold, agentId, limit = 10, filters, createdAtFilter, expiresAtFilter } = options;
631
647
  // Create embedding for the query
632
648
  const embedding = await this.createEmbedding(query);
633
649
  const embeddingStr = `[${embedding.join(',')}]`;
@@ -638,10 +654,10 @@ class MeshOS {
638
654
  agent_id
639
655
  content
640
656
  metadata
641
- embedding
642
657
  similarity
643
658
  created_at
644
659
  updated_at
660
+ expires_at
645
661
  }
646
662
  }
647
663
  `;
@@ -677,7 +693,9 @@ class MeshOS {
677
693
  match_threshold: threshold,
678
694
  match_count: limit,
679
695
  filter_agent_id: agentId,
680
- metadata_filter: metadataFilter
696
+ metadata_filter: metadataFilter,
697
+ created_at_filter: createdAtFilter,
698
+ expires_at_filter: expiresAtFilter
681
699
  }
682
700
  });
683
701
  return result.search_memories.map(memory => ({
@@ -688,7 +706,8 @@ class MeshOS {
688
706
  embedding: memory.embedding,
689
707
  similarity: memory.similarity,
690
708
  createdAt: memory.created_at,
691
- updatedAt: memory.updated_at
709
+ updatedAt: memory.updated_at,
710
+ expiresAt: memory.expires_at
692
711
  }));
693
712
  }
694
713
  /**
@@ -719,5 +738,56 @@ class MeshOS {
719
738
  }
720
739
  return result.update_agents_by_pk;
721
740
  }
741
+ /**
742
+ * Query memories with flexible filtering, sorting, and pagination.
743
+ */
744
+ async getMemories(options) {
745
+ const { where, orderBy, limit, offset, distinct, distinctOn } = options;
746
+ // Build the GraphQL query dynamically
747
+ const query = `
748
+ query GetMemories(
749
+ $where: memories_bool_exp,
750
+ $orderBy: [memories_order_by!],
751
+ $limit: Int,
752
+ $offset: Int,
753
+ $distinctOn: [memories_select_column!]
754
+ ) {
755
+ memories(
756
+ where: $where,
757
+ order_by: $orderBy,
758
+ limit: $limit,
759
+ offset: $offset,
760
+ distinct: ${distinct ? 'true' : 'false'},
761
+ distinct_on: $distinctOn
762
+ ) {
763
+ id
764
+ agent_id
765
+ content
766
+ metadata
767
+ embedding
768
+ created_at
769
+ updated_at
770
+ expires_at
771
+ }
772
+ }
773
+ `;
774
+ const result = await this.executeQuery(query, {
775
+ where,
776
+ orderBy,
777
+ limit,
778
+ offset,
779
+ distinctOn
780
+ });
781
+ return result.memories.map(memory => ({
782
+ id: memory.id,
783
+ agentId: memory.agent_id,
784
+ content: memory.content,
785
+ metadata: memory.metadata,
786
+ embedding: memory.embedding,
787
+ createdAt: memory.created_at,
788
+ updatedAt: memory.updated_at,
789
+ expiresAt: memory.expires_at
790
+ }));
791
+ }
722
792
  }
723
793
  exports.MeshOS = MeshOS;
@@ -229,3 +229,13 @@ export declare const edgeMetadataSchema: z.ZodObject<{
229
229
  bidirectional?: boolean | undefined;
230
230
  }>;
231
231
  export type EdgeMetadata = z.infer<typeof edgeMetadataSchema>;
232
+ /**
233
+ * Timestamp filter operators
234
+ */
235
+ export interface TimestampFilter {
236
+ _gt?: string;
237
+ _gte?: string;
238
+ _lt?: string;
239
+ _lte?: string;
240
+ _eq?: string;
241
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@props-labs/mesh-os",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "MeshOS - A memory system for AI agents",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,55 @@
1
+ -- Drop the updated search_memories function
2
+ DROP FUNCTION IF EXISTS public.search_memories(vector(1536), float8, integer, uuid, jsonb, jsonb, jsonb);
3
+
4
+ -- Remove expires_at column
5
+ ALTER TABLE public.memories DROP COLUMN expires_at;
6
+
7
+ -- Drop and recreate the view without expires_at
8
+ DROP VIEW IF EXISTS public.memories_with_similarity;
9
+ CREATE OR REPLACE VIEW public.memories_with_similarity AS
10
+ SELECT
11
+ m.*,
12
+ 0::float8 as similarity
13
+ FROM memories m;
14
+
15
+ -- Restore the previous version of search_memories from 2_metadata_filtering
16
+ CREATE OR REPLACE FUNCTION public.search_memories(
17
+ query_embedding vector(1536),
18
+ match_threshold float8,
19
+ match_count integer,
20
+ filter_agent_id uuid DEFAULT NULL,
21
+ metadata_filter jsonb DEFAULT NULL
22
+ )
23
+ RETURNS SETOF public.memories_with_similarity
24
+ LANGUAGE sql
25
+ STABLE
26
+ AS $$
27
+ WITH normalized_query AS (
28
+ SELECT l2_normalize(query_embedding) AS normalized_vector
29
+ )
30
+ SELECT
31
+ m.id,
32
+ m.agent_id,
33
+ m.content,
34
+ m.metadata,
35
+ m.embedding,
36
+ m.created_at,
37
+ m.updated_at,
38
+ -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) as similarity
39
+ FROM memories m
40
+ WHERE
41
+ CASE
42
+ WHEN filter_agent_id IS NOT NULL THEN m.agent_id = filter_agent_id
43
+ ELSE TRUE
44
+ END
45
+ AND CASE
46
+ WHEN metadata_filter IS NOT NULL THEN m.metadata @> metadata_filter
47
+ ELSE TRUE
48
+ END
49
+ AND -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) >= match_threshold
50
+ ORDER BY -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) DESC
51
+ LIMIT match_count;
52
+ $$;
53
+
54
+ -- Track the function in Hasura
55
+ COMMENT ON FUNCTION public.search_memories IS E'@graphql({"type": "Query"})';
@@ -0,0 +1,108 @@
1
+ -- Add expires_at column to memories table
2
+ ALTER TABLE public.memories ADD COLUMN expires_at TIMESTAMPTZ;
3
+
4
+ -- Create a view for memories with similarity that includes all fields
5
+ DROP VIEW IF EXISTS public.memories_with_similarity;
6
+ CREATE OR REPLACE VIEW public.memories_with_similarity AS
7
+ SELECT
8
+ m.*,
9
+ 0::float8 as similarity -- Default similarity, will be replaced in search
10
+ FROM memories m;
11
+
12
+ -- Drop the existing search_memories function
13
+ DROP FUNCTION IF EXISTS public.search_memories;
14
+
15
+ -- Create the updated search_memories function with standard Hasura filtering
16
+ CREATE OR REPLACE FUNCTION public.search_memories(
17
+ query_embedding vector(1536),
18
+ match_threshold float8,
19
+ match_count integer,
20
+ filter_agent_id uuid DEFAULT NULL,
21
+ metadata_filter jsonb DEFAULT NULL,
22
+ created_at_filter jsonb DEFAULT NULL,
23
+ expires_at_filter jsonb DEFAULT NULL
24
+ )
25
+ RETURNS SETOF public.memories_with_similarity
26
+ LANGUAGE sql
27
+ STABLE
28
+ AS $$
29
+ WITH normalized_query AS (
30
+ SELECT l2_normalize(query_embedding) AS normalized_vector
31
+ )
32
+ SELECT
33
+ m.id,
34
+ m.agent_id,
35
+ m.content,
36
+ m.metadata,
37
+ m.embedding,
38
+ m.created_at,
39
+ m.updated_at,
40
+ m.expires_at,
41
+ -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) as similarity
42
+ FROM memories m
43
+ WHERE
44
+ CASE
45
+ WHEN filter_agent_id IS NOT NULL THEN m.agent_id = filter_agent_id
46
+ ELSE TRUE
47
+ END
48
+ AND CASE
49
+ WHEN metadata_filter IS NOT NULL THEN m.metadata @> metadata_filter
50
+ ELSE TRUE
51
+ END
52
+ AND CASE
53
+ WHEN created_at_filter IS NOT NULL THEN (
54
+ CASE
55
+ WHEN created_at_filter ? '_gt' THEN m.created_at > (created_at_filter->>'_gt')::timestamptz
56
+ ELSE TRUE
57
+ END
58
+ AND CASE
59
+ WHEN created_at_filter ? '_gte' THEN m.created_at >= (created_at_filter->>'_gte')::timestamptz
60
+ ELSE TRUE
61
+ END
62
+ AND CASE
63
+ WHEN created_at_filter ? '_lt' THEN m.created_at < (created_at_filter->>'_lt')::timestamptz
64
+ ELSE TRUE
65
+ END
66
+ AND CASE
67
+ WHEN created_at_filter ? '_lte' THEN m.created_at <= (created_at_filter->>'_lte')::timestamptz
68
+ ELSE TRUE
69
+ END
70
+ AND CASE
71
+ WHEN created_at_filter ? '_eq' THEN m.created_at = (created_at_filter->>'_eq')::timestamptz
72
+ ELSE TRUE
73
+ END
74
+ )
75
+ ELSE TRUE
76
+ END
77
+ AND CASE
78
+ WHEN expires_at_filter IS NOT NULL THEN (
79
+ CASE
80
+ WHEN expires_at_filter ? '_gt' THEN m.expires_at > (expires_at_filter->>'_gt')::timestamptz
81
+ ELSE TRUE
82
+ END
83
+ AND CASE
84
+ WHEN expires_at_filter ? '_gte' THEN m.expires_at >= (expires_at_filter->>'_gte')::timestamptz
85
+ ELSE TRUE
86
+ END
87
+ AND CASE
88
+ WHEN expires_at_filter ? '_lt' THEN m.expires_at < (expires_at_filter->>'_lt')::timestamptz
89
+ ELSE TRUE
90
+ END
91
+ AND CASE
92
+ WHEN expires_at_filter ? '_lte' THEN m.expires_at <= (expires_at_filter->>'_lte')::timestamptz
93
+ ELSE TRUE
94
+ END
95
+ AND CASE
96
+ WHEN expires_at_filter ? '_eq' THEN m.expires_at = (expires_at_filter->>'_eq')::timestamptz
97
+ ELSE TRUE
98
+ END
99
+ )
100
+ ELSE TRUE
101
+ END
102
+ AND -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) >= match_threshold
103
+ ORDER BY -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) DESC
104
+ LIMIT match_count;
105
+ $$;
106
+
107
+ -- Track the function in Hasura
108
+ COMMENT ON FUNCTION public.search_memories IS E'@graphql({"type": "Query"})';