@props-labs/mesh-os 0.1.17 → 0.1.18

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.
@@ -1,4 +1,5 @@
1
- import { EdgeType, type AgentStatus, type EdgeMetadata, type MemoryMetadata, type TimestampFilter } from './taxonomy';
1
+ import { EdgeType, type AgentStatus, type EdgeMetadata, type MemoryMetadata, type TimestampFilter, EntityRelationshipType } from './taxonomy';
2
+ import { EntityManager } from './entities';
2
3
  /**
3
4
  * An agent in the system.
4
5
  */
@@ -56,6 +57,17 @@ export interface MeshOSConfig {
56
57
  apiKey?: string;
57
58
  openaiApiKey?: string;
58
59
  }
60
+ /**
61
+ * Memory creation options with entity support.
62
+ */
63
+ export interface RememberOptions {
64
+ content: string;
65
+ agentId: string;
66
+ metadata?: Partial<MemoryMetadata>;
67
+ expiresAt?: string;
68
+ entityId?: string;
69
+ entityRelationship?: EntityRelationshipType;
70
+ }
59
71
  /**
60
72
  * MeshOS client for interacting with the system.
61
73
  */
@@ -63,6 +75,7 @@ export declare class MeshOS {
63
75
  private url;
64
76
  private headers;
65
77
  private openai;
78
+ entities: EntityManager;
66
79
  constructor(config?: MeshOSConfig);
67
80
  private validateSlug;
68
81
  /**
@@ -90,9 +103,13 @@ export declare class MeshOS {
90
103
  */
91
104
  getAgent(agentId: string): Promise<Agent | null>;
92
105
  /**
93
- * Store a new memory.
106
+ * Store a new memory with optional entity linking.
107
+ */
108
+ remember(options: RememberOptions | string, agentId?: string, metadata?: Partial<MemoryMetadata>, expiresAt?: string): Promise<Memory | Memory[]>;
109
+ /**
110
+ * Internal method to store a memory.
94
111
  */
95
- remember(content: string, agentId: string, metadata?: Partial<MemoryMetadata>, expiresAt?: string): Promise<Memory | Memory[]>;
112
+ private _remember;
96
113
  /**
97
114
  * Delete a specific memory.
98
115
  */
@@ -124,7 +141,7 @@ export declare class MeshOS {
124
141
  */
125
142
  private expandQuery;
126
143
  /**
127
- * Search memories by semantic similarity.
144
+ * Search memories by semantic similarity with optional entity filtering.
128
145
  */
129
146
  recall(options: {
130
147
  query: string;
@@ -137,6 +154,8 @@ export declare class MeshOS {
137
154
  filters?: Record<string, unknown>;
138
155
  createdAtFilter?: TimestampFilter;
139
156
  expiresAtFilter?: TimestampFilter;
157
+ entityId?: string;
158
+ entityRelationship?: EntityRelationshipType;
140
159
  }): Promise<Memory[]>;
141
160
  /**
142
161
  * Internal method to perform recall with a specific threshold.
@@ -11,6 +11,7 @@ const openai_1 = __importDefault(require("openai"));
11
11
  const chalk_1 = __importDefault(require("chalk"));
12
12
  const boxen_1 = __importDefault(require("boxen"));
13
13
  const taxonomy_1 = require("./taxonomy");
14
+ const entities_1 = require("./entities");
14
15
  // Constants
15
16
  const SLUG_PATTERN = /^[a-zA-Z0-9_-]+$/;
16
17
  /**
@@ -60,6 +61,8 @@ class MeshOS {
60
61
  throw new Error('OpenAI API key is required');
61
62
  }
62
63
  this.openai = new openai_1.default({ apiKey: openaiApiKey });
64
+ // Initialize entity manager
65
+ this.entities = new entities_1.EntityManager(this.url, this.headers, this.createEmbedding.bind(this));
63
66
  }
64
67
  validateSlug(slug) {
65
68
  return SLUG_PATTERN.test(slug);
@@ -195,9 +198,32 @@ class MeshOS {
195
198
  return result.agents_by_pk;
196
199
  }
197
200
  /**
198
- * Store a new memory.
201
+ * Store a new memory with optional entity linking.
199
202
  */
200
- async remember(content, agentId, metadata, expiresAt) {
203
+ async remember(options, agentId, metadata, expiresAt) {
204
+ // Handle legacy method signature
205
+ if (typeof options === 'string') {
206
+ options = {
207
+ content: options,
208
+ agentId: agentId,
209
+ metadata,
210
+ expiresAt
211
+ };
212
+ }
213
+ // Create the memory
214
+ const result = await this._remember(options.content, options.agentId, options.metadata, options.expiresAt);
215
+ // If entity linking is requested, create the link
216
+ if (options.entityId) {
217
+ const memories = Array.isArray(result) ? result : [result];
218
+ const firstMemory = memories[0];
219
+ await this.entities.linkMemory(options.entityId, firstMemory.id, options.entityRelationship || taxonomy_1.EntityRelationshipType.ASSOCIATED);
220
+ }
221
+ return result;
222
+ }
223
+ /**
224
+ * Internal method to store a memory.
225
+ */
226
+ async _remember(content, agentId, metadata, expiresAt) {
201
227
  // Create default metadata if none provided
202
228
  const fullMetadata = taxonomy_1.memoryMetadataSchema.parse({
203
229
  type: taxonomy_1.DataType.KNOWLEDGE,
@@ -528,36 +554,42 @@ class MeshOS {
528
554
  return result.slice(0, numVariations + 1);
529
555
  }
530
556
  /**
531
- * Search memories by semantic similarity.
557
+ * Search memories by semantic similarity with optional entity filtering.
532
558
  */
533
559
  async recall(options) {
534
- const { query, agentId, limit = 5, threshold = 0.7, minResults = 1, adaptiveThreshold = true, useSemanticExpansion = true, filters, createdAtFilter, expiresAtFilter } = options;
535
- // First try: Direct search with initial threshold
560
+ const minResults = options.minResults || 1;
536
561
  let results = await this.recallWithThreshold({
537
- query,
538
- threshold,
539
- agentId,
540
- limit,
541
- filters,
542
- createdAtFilter,
543
- expiresAtFilter
562
+ query: options.query,
563
+ threshold: options.threshold || 0.7,
564
+ agentId: options.agentId,
565
+ limit: options.limit,
566
+ filters: options.filters,
567
+ createdAtFilter: options.createdAtFilter,
568
+ expiresAtFilter: options.expiresAtFilter
544
569
  });
570
+ // If entity filtering is requested, filter results
571
+ if (options.entityId) {
572
+ const linkedMemories = await this.entities.getLinkedMemories(options.entityId, options.entityRelationship);
573
+ const linkedMemoryIds = new Set(linkedMemories.map(m => m.id));
574
+ results = results.filter(memory => linkedMemoryIds.has(memory.id));
575
+ }
576
+ // First try: Direct search with initial threshold
545
577
  if (results.length >= minResults) {
546
- return results.slice(0, limit);
578
+ return results.slice(0, options.limit || 5);
547
579
  }
548
580
  // Second try: If adaptive threshold is enabled, try lowering the threshold
549
- if (adaptiveThreshold) {
550
- let currentThreshold = threshold - 0.05;
581
+ if (options.adaptiveThreshold) {
582
+ let currentThreshold = (options.threshold || 0.7) - 0.05;
551
583
  const minThreshold = 0.3; // Don't go below this to avoid irrelevant matches
552
584
  while (currentThreshold >= minThreshold && results.length < minResults) {
553
585
  const newResults = await this.recallWithThreshold({
554
- query,
586
+ query: options.query,
555
587
  threshold: currentThreshold,
556
- agentId,
557
- limit,
558
- filters,
559
- createdAtFilter,
560
- expiresAtFilter
588
+ agentId: options.agentId,
589
+ limit: options.limit,
590
+ filters: options.filters,
591
+ createdAtFilter: options.createdAtFilter,
592
+ expiresAtFilter: options.expiresAtFilter
561
593
  });
562
594
  // Add new results that aren't already in the list
563
595
  for (const result of newResults) {
@@ -572,20 +604,20 @@ class MeshOS {
572
604
  }
573
605
  }
574
606
  // Third try: If we still don't have ANY results and semantic expansion is enabled
575
- if (results.length === 0 && useSemanticExpansion) {
576
- const variations = await this.expandQuery(query);
607
+ if (results.length === 0 && options.useSemanticExpansion) {
608
+ const variations = await this.expandQuery(options.query);
577
609
  const seenIds = new Map();
578
610
  const minThreshold = 0.3; // Don't go below this to avoid irrelevant matches
579
611
  // Try each variation with the original threshold first
580
612
  for (const variation of variations.slice(1)) { // Skip original query as we already tried it
581
613
  const variationResults = await this.recallWithThreshold({
582
614
  query: variation,
583
- threshold,
584
- agentId,
585
- limit,
586
- filters,
587
- createdAtFilter,
588
- expiresAtFilter
615
+ threshold: options.threshold || 0.7,
616
+ agentId: options.agentId,
617
+ limit: options.limit,
618
+ filters: options.filters,
619
+ createdAtFilter: options.createdAtFilter,
620
+ expiresAtFilter: options.expiresAtFilter
589
621
  });
590
622
  // Add new results or update if better similarity
591
623
  for (const memory of variationResults) {
@@ -599,18 +631,18 @@ class MeshOS {
599
631
  }
600
632
  }
601
633
  // If still no results, try variations with adaptive threshold
602
- if (seenIds.size === 0 && adaptiveThreshold) {
603
- let currentThreshold = threshold - 0.05;
634
+ if (seenIds.size === 0 && options.adaptiveThreshold) {
635
+ let currentThreshold = (options.threshold || 0.7) - 0.05;
604
636
  while (currentThreshold >= minThreshold && seenIds.size === 0) {
605
637
  for (const variation of variations.slice(1)) {
606
638
  const variationResults = await this.recallWithThreshold({
607
639
  query: variation,
608
640
  threshold: currentThreshold,
609
- agentId,
610
- limit,
611
- filters,
612
- createdAtFilter,
613
- expiresAtFilter
641
+ agentId: options.agentId,
642
+ limit: options.limit,
643
+ filters: options.filters,
644
+ createdAtFilter: options.createdAtFilter,
645
+ expiresAtFilter: options.expiresAtFilter
614
646
  });
615
647
  for (const memory of variationResults) {
616
648
  const existingMemory = seenIds.get(memory.id);
@@ -635,7 +667,7 @@ class MeshOS {
635
667
  }
636
668
  // Sort by similarity and return top results
637
669
  results.sort((a, b) => (b.similarity || 0) - (a.similarity || 0));
638
- return results.slice(0, limit);
670
+ return results.slice(0, options.limit || 5);
639
671
  }
640
672
  /**
641
673
  * Internal method to perform recall with a specific threshold.
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Entity management functionality for MeshOS.
3
+ */
4
+ import { Entity, EntityStatus, EntityRelationshipType, EntityMemoryLink } from './taxonomy';
5
+ import type { Memory } from './client';
6
+ export declare class EntityManager {
7
+ private url;
8
+ private headers;
9
+ private createEmbedding;
10
+ constructor(url: string, headers: Record<string, string>, createEmbedding: (text: string) => Promise<number[]>);
11
+ /**
12
+ * Execute a GraphQL query.
13
+ */
14
+ private executeQuery;
15
+ /**
16
+ * Add a new entity.
17
+ */
18
+ add(entity: Omit<Entity, 'id' | 'createdAt' | 'updatedAt'>): Promise<Entity>;
19
+ /**
20
+ * Get an entity by ID.
21
+ */
22
+ get(id: string): Promise<Entity | null>;
23
+ /**
24
+ * Get an entity by reference ID.
25
+ */
26
+ getByRefId(refId: string): Promise<Entity | null>;
27
+ /**
28
+ * Update an entity.
29
+ */
30
+ update(id: string, updates: Partial<Omit<Entity, 'id' | 'createdAt' | 'updatedAt'>>): Promise<Entity>;
31
+ /**
32
+ * Delete an entity.
33
+ */
34
+ delete(id: string): Promise<boolean>;
35
+ /**
36
+ * Search entities by semantic similarity.
37
+ */
38
+ search(options: {
39
+ query: string;
40
+ threshold?: number;
41
+ limit?: number;
42
+ type?: string;
43
+ status?: EntityStatus;
44
+ }): Promise<(Entity & {
45
+ similarity?: number;
46
+ })[]>;
47
+ /**
48
+ * Link an entity to a memory.
49
+ */
50
+ linkMemory(entityId: string, memoryId: string, relationship: EntityRelationshipType, confidence?: number, metadata?: Record<string, unknown>): Promise<EntityMemoryLink>;
51
+ /**
52
+ * Get all memories linked to an entity.
53
+ */
54
+ getLinkedMemories(entityId: string, relationship?: EntityRelationshipType): Promise<Array<Memory & {
55
+ relationship: EntityRelationshipType;
56
+ confidence: number;
57
+ }>>;
58
+ }
@@ -0,0 +1,349 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EntityManager = void 0;
4
+ /**
5
+ * Entity management functionality for MeshOS.
6
+ */
7
+ const taxonomy_1 = require("./taxonomy");
8
+ class EntityManager {
9
+ constructor(url, headers, createEmbedding) {
10
+ this.url = url;
11
+ this.headers = headers;
12
+ this.createEmbedding = createEmbedding;
13
+ }
14
+ /**
15
+ * Execute a GraphQL query.
16
+ */
17
+ async executeQuery(query, variables) {
18
+ const response = await fetch(this.url, {
19
+ method: 'POST',
20
+ headers: this.headers,
21
+ body: JSON.stringify({
22
+ query,
23
+ variables: variables || {}
24
+ })
25
+ });
26
+ if (!response.ok) {
27
+ throw new Error(`HTTP error! status: ${response.status}`);
28
+ }
29
+ const result = await response.json();
30
+ if (result.errors) {
31
+ throw new Error(result.errors[0].message);
32
+ }
33
+ return result.data;
34
+ }
35
+ /**
36
+ * Add a new entity.
37
+ */
38
+ async add(entity) {
39
+ // Validate entity data
40
+ const validatedData = taxonomy_1.entitySchema.omit({ id: true, createdAt: true, updatedAt: true }).parse(entity);
41
+ // Create embedding from name and description
42
+ const embeddingText = `${validatedData.name} ${validatedData.description || ''}`.trim();
43
+ const embedding = await this.createEmbedding(embeddingText);
44
+ const embeddingStr = `[${embedding.join(',')}]`;
45
+ const query = `
46
+ mutation AddEntity($entity: entities_insert_input!) {
47
+ insert_entities_one(object: $entity) {
48
+ id
49
+ ref_id
50
+ type
51
+ name
52
+ description
53
+ metadata
54
+ status
55
+ created_at
56
+ updated_at
57
+ }
58
+ }
59
+ `;
60
+ const result = await this.executeQuery(query, {
61
+ entity: {
62
+ ref_id: validatedData.refId,
63
+ type: validatedData.type,
64
+ name: validatedData.name,
65
+ description: validatedData.description,
66
+ metadata: validatedData.metadata,
67
+ status: validatedData.status,
68
+ embedding: embeddingStr
69
+ }
70
+ });
71
+ // Convert snake_case to camelCase
72
+ const { ref_id, created_at, updated_at, ...rest } = result.insert_entities_one;
73
+ return {
74
+ ...rest,
75
+ refId: ref_id,
76
+ createdAt: created_at,
77
+ updatedAt: updated_at,
78
+ };
79
+ }
80
+ /**
81
+ * Get an entity by ID.
82
+ */
83
+ async get(id) {
84
+ const query = `
85
+ query GetEntity($id: uuid!) {
86
+ entities_by_pk(id: $id) {
87
+ id
88
+ ref_id
89
+ type
90
+ name
91
+ description
92
+ metadata
93
+ status
94
+ created_at
95
+ updated_at
96
+ }
97
+ }
98
+ `;
99
+ const result = await this.executeQuery(query, { id });
100
+ if (!result.entities_by_pk) {
101
+ return null;
102
+ }
103
+ // Convert snake_case to camelCase
104
+ const { ref_id, created_at, updated_at, ...rest } = result.entities_by_pk;
105
+ return {
106
+ ...rest,
107
+ refId: ref_id,
108
+ createdAt: created_at,
109
+ updatedAt: updated_at,
110
+ };
111
+ }
112
+ /**
113
+ * Get an entity by reference ID.
114
+ */
115
+ async getByRefId(refId) {
116
+ const query = `
117
+ query GetEntityByRefId($refId: String!) {
118
+ entities(where: {ref_id: {_eq: $refId}}, limit: 1) {
119
+ id
120
+ ref_id
121
+ type
122
+ name
123
+ description
124
+ metadata
125
+ status
126
+ created_at
127
+ updated_at
128
+ }
129
+ }
130
+ `;
131
+ const result = await this.executeQuery(query, { refId });
132
+ if (!result.entities[0]) {
133
+ return null;
134
+ }
135
+ // Convert snake_case to camelCase
136
+ const { ref_id, created_at, updated_at, ...rest } = result.entities[0];
137
+ return {
138
+ ...rest,
139
+ refId: ref_id,
140
+ createdAt: created_at,
141
+ updatedAt: updated_at,
142
+ };
143
+ }
144
+ /**
145
+ * Update an entity.
146
+ */
147
+ async update(id, updates) {
148
+ // If name or description is updated, update embedding
149
+ let embedding;
150
+ if (updates.name || updates.description) {
151
+ const entity = await this.get(id);
152
+ if (!entity) {
153
+ throw new Error(`Entity ${id} not found`);
154
+ }
155
+ const embeddingText = `${updates.name || entity.name} ${updates.description || entity.description || ''}`.trim();
156
+ embedding = await this.createEmbedding(embeddingText);
157
+ }
158
+ const query = `
159
+ mutation UpdateEntity($id: uuid!, $updates: entities_set_input!) {
160
+ update_entities_by_pk(pk_columns: {id: $id}, _set: $updates) {
161
+ id
162
+ ref_id
163
+ type
164
+ name
165
+ description
166
+ metadata
167
+ status
168
+ created_at
169
+ updated_at
170
+ }
171
+ }
172
+ `;
173
+ const updateData = {
174
+ ref_id: updates.refId,
175
+ type: updates.type,
176
+ name: updates.name,
177
+ description: updates.description,
178
+ metadata: updates.metadata,
179
+ status: updates.status,
180
+ };
181
+ if (embedding) {
182
+ updateData.embedding = `[${embedding.join(',')}]`;
183
+ }
184
+ // Remove undefined values
185
+ Object.keys(updateData).forEach(key => updateData[key] === undefined && delete updateData[key]);
186
+ const result = await this.executeQuery(query, {
187
+ id,
188
+ updates: updateData
189
+ });
190
+ // Convert snake_case to camelCase
191
+ const { ref_id, created_at, updated_at, ...rest } = result.update_entities_by_pk;
192
+ return {
193
+ ...rest,
194
+ refId: ref_id,
195
+ createdAt: created_at,
196
+ updatedAt: updated_at,
197
+ };
198
+ }
199
+ /**
200
+ * Delete an entity.
201
+ */
202
+ async delete(id) {
203
+ const query = `
204
+ mutation DeleteEntity($id: uuid!) {
205
+ delete_entities_by_pk(id: $id) {
206
+ id
207
+ }
208
+ }
209
+ `;
210
+ const result = await this.executeQuery(query, { id });
211
+ return result.delete_entities_by_pk !== null;
212
+ }
213
+ /**
214
+ * Search entities by semantic similarity.
215
+ */
216
+ async search(options) {
217
+ const { query, threshold = 0.7, limit = 10, type, status } = options;
218
+ // Create embedding for search query
219
+ const embedding = await this.createEmbedding(query);
220
+ const embeddingStr = `[${embedding.join(',')}]`;
221
+ const searchQuery = `
222
+ query SearchEntities(
223
+ $embedding: vector!,
224
+ $threshold: float8!,
225
+ $limit: Int!,
226
+ $type: String,
227
+ $status: String
228
+ ) {
229
+ entities(
230
+ where: {
231
+ _and: [
232
+ {type: {_eq: $type}},
233
+ {status: {_eq: $status}}
234
+ ]
235
+ },
236
+ order_by: {embedding: {_cosine_distance: $embedding}},
237
+ limit: $limit
238
+ ) {
239
+ id
240
+ ref_id
241
+ type
242
+ name
243
+ description
244
+ metadata
245
+ status
246
+ created_at
247
+ updated_at
248
+ similarity: _cosine_similarity_embedding_with($embedding)
249
+ }
250
+ }
251
+ `;
252
+ const result = await this.executeQuery(searchQuery, {
253
+ embedding: embeddingStr,
254
+ threshold,
255
+ limit,
256
+ type,
257
+ status
258
+ });
259
+ return result.entities.map(entity => {
260
+ const { ref_id, created_at, updated_at, ...rest } = entity;
261
+ return {
262
+ ...rest,
263
+ refId: ref_id,
264
+ createdAt: created_at,
265
+ updatedAt: updated_at,
266
+ };
267
+ }).filter(entity => (entity.similarity || 0) >= threshold);
268
+ }
269
+ /**
270
+ * Link an entity to a memory.
271
+ */
272
+ async linkMemory(entityId, memoryId, relationship, confidence, metadata) {
273
+ const query = `
274
+ mutation LinkEntityMemory($link: entity_memory_links_insert_input!) {
275
+ insert_entity_memory_links_one(object: $link) {
276
+ id
277
+ entity_id
278
+ memory_id
279
+ relationship
280
+ confidence
281
+ metadata
282
+ created_at
283
+ }
284
+ }
285
+ `;
286
+ const result = await this.executeQuery(query, {
287
+ link: {
288
+ entity_id: entityId,
289
+ memory_id: memoryId,
290
+ relationship,
291
+ confidence: confidence || 1.0,
292
+ metadata: metadata || {}
293
+ }
294
+ });
295
+ // Convert snake_case to camelCase
296
+ const { entity_id, memory_id, created_at, ...rest } = result.insert_entity_memory_links_one;
297
+ return {
298
+ ...rest,
299
+ entityId: entity_id,
300
+ memoryId: memory_id,
301
+ createdAt: created_at,
302
+ };
303
+ }
304
+ /**
305
+ * Get all memories linked to an entity.
306
+ */
307
+ async getLinkedMemories(entityId, relationship) {
308
+ const query = `
309
+ query GetLinkedMemories($entityId: uuid!, $relationship: String) {
310
+ entity_memory_links(
311
+ where: {
312
+ entity_id: {_eq: $entityId},
313
+ relationship: {_eq: $relationship}
314
+ }
315
+ ) {
316
+ memory {
317
+ id
318
+ agent_id
319
+ content
320
+ metadata
321
+ created_at
322
+ updated_at
323
+ expires_at
324
+ }
325
+ relationship
326
+ confidence
327
+ }
328
+ }
329
+ `;
330
+ const result = await this.executeQuery(query, {
331
+ entityId,
332
+ relationship
333
+ });
334
+ return result.entity_memory_links.map(link => {
335
+ const { memory, relationship, confidence } = link;
336
+ const { agent_id, created_at, updated_at, expires_at, ...rest } = memory;
337
+ return {
338
+ ...rest,
339
+ agentId: agent_id,
340
+ createdAt: created_at,
341
+ updatedAt: updated_at,
342
+ expiresAt: expires_at,
343
+ relationship,
344
+ confidence
345
+ };
346
+ });
347
+ }
348
+ }
349
+ exports.EntityManager = EntityManager;
@@ -11,6 +11,90 @@ export declare enum DataType {
11
11
  DECISION = "decision",// Strategic & operational choices
12
12
  MEDIA = "media"
13
13
  }
14
+ /**
15
+ * Entity relationship types.
16
+ */
17
+ export declare enum EntityRelationshipType {
18
+ ASSOCIATED = "associated",// Generic association
19
+ OWNS = "owns",// Ownership relationship
20
+ REFERENCES = "references",// Reference relationship
21
+ MENTIONS = "mentions",// Mention relationship
22
+ DESCRIBES = "describes",// Description relationship
23
+ PART_OF = "part_of"
24
+ }
25
+ /**
26
+ * Entity status types.
27
+ */
28
+ export declare enum EntityStatus {
29
+ ACTIVE = "active",
30
+ INACTIVE = "inactive",
31
+ ARCHIVED = "archived",
32
+ DELETED = "deleted"
33
+ }
34
+ /**
35
+ * Entity schema definition.
36
+ */
37
+ export declare const entitySchema: z.ZodObject<{
38
+ id: z.ZodOptional<z.ZodString>;
39
+ refId: z.ZodOptional<z.ZodString>;
40
+ type: z.ZodString;
41
+ name: z.ZodString;
42
+ description: z.ZodOptional<z.ZodString>;
43
+ metadata: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
44
+ status: z.ZodDefault<z.ZodNativeEnum<typeof EntityStatus>>;
45
+ createdAt: z.ZodOptional<z.ZodString>;
46
+ updatedAt: z.ZodOptional<z.ZodString>;
47
+ }, "strip", z.ZodTypeAny, {
48
+ type: string;
49
+ name: string;
50
+ status: EntityStatus;
51
+ metadata: Record<string, unknown>;
52
+ id?: string | undefined;
53
+ refId?: string | undefined;
54
+ description?: string | undefined;
55
+ createdAt?: string | undefined;
56
+ updatedAt?: string | undefined;
57
+ }, {
58
+ type: string;
59
+ name: string;
60
+ id?: string | undefined;
61
+ refId?: string | undefined;
62
+ description?: string | undefined;
63
+ status?: EntityStatus | undefined;
64
+ metadata?: Record<string, unknown> | undefined;
65
+ createdAt?: string | undefined;
66
+ updatedAt?: string | undefined;
67
+ }>;
68
+ export type Entity = z.infer<typeof entitySchema>;
69
+ /**
70
+ * Entity-memory link schema definition.
71
+ */
72
+ export declare const entityMemoryLinkSchema: z.ZodObject<{
73
+ id: z.ZodOptional<z.ZodString>;
74
+ entityId: z.ZodString;
75
+ memoryId: z.ZodString;
76
+ relationship: z.ZodNativeEnum<typeof EntityRelationshipType>;
77
+ confidence: z.ZodDefault<z.ZodNumber>;
78
+ metadata: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
79
+ createdAt: z.ZodOptional<z.ZodString>;
80
+ }, "strip", z.ZodTypeAny, {
81
+ metadata: Record<string, unknown>;
82
+ entityId: string;
83
+ memoryId: string;
84
+ relationship: EntityRelationshipType;
85
+ confidence: number;
86
+ id?: string | undefined;
87
+ createdAt?: string | undefined;
88
+ }, {
89
+ entityId: string;
90
+ memoryId: string;
91
+ relationship: EntityRelationshipType;
92
+ id?: string | undefined;
93
+ metadata?: Record<string, unknown> | undefined;
94
+ createdAt?: string | undefined;
95
+ confidence?: number | undefined;
96
+ }>;
97
+ export type EntityMemoryLink = z.infer<typeof entityMemoryLinkSchema>;
14
98
  /**
15
99
  * Subtypes for activity data.
16
100
  */
@@ -216,8 +300,8 @@ export declare const edgeMetadataSchema: z.ZodObject<{
216
300
  bidirectional: z.ZodDefault<z.ZodBoolean>;
217
301
  additional: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
218
302
  }, "strip", z.ZodTypeAny, {
219
- additional: Record<string, unknown>;
220
303
  relationship: EdgeType;
304
+ additional: Record<string, unknown>;
221
305
  weight: number;
222
306
  bidirectional: boolean;
223
307
  validUntil?: Date | undefined;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.edgeMetadataSchema = exports.memoryMetadataSchema = exports.CoreAgentStatus = exports.RelevanceTag = exports.EdgeType = exports.MediaSubtype = exports.DecisionSubtype = exports.KnowledgeSubtype = exports.ActivitySubtype = exports.DataType = void 0;
3
+ exports.edgeMetadataSchema = exports.memoryMetadataSchema = exports.CoreAgentStatus = exports.RelevanceTag = exports.EdgeType = exports.MediaSubtype = exports.DecisionSubtype = exports.KnowledgeSubtype = exports.ActivitySubtype = exports.entityMemoryLinkSchema = exports.entitySchema = exports.EntityStatus = exports.EntityRelationshipType = exports.DataType = void 0;
4
4
  /**
5
5
  * Taxonomy and classification models for MeshOS.
6
6
  */
@@ -15,6 +15,54 @@ var DataType;
15
15
  DataType["DECISION"] = "decision";
16
16
  DataType["MEDIA"] = "media";
17
17
  })(DataType || (exports.DataType = DataType = {}));
18
+ /**
19
+ * Entity relationship types.
20
+ */
21
+ var EntityRelationshipType;
22
+ (function (EntityRelationshipType) {
23
+ EntityRelationshipType["ASSOCIATED"] = "associated";
24
+ EntityRelationshipType["OWNS"] = "owns";
25
+ EntityRelationshipType["REFERENCES"] = "references";
26
+ EntityRelationshipType["MENTIONS"] = "mentions";
27
+ EntityRelationshipType["DESCRIBES"] = "describes";
28
+ EntityRelationshipType["PART_OF"] = "part_of";
29
+ })(EntityRelationshipType || (exports.EntityRelationshipType = EntityRelationshipType = {}));
30
+ /**
31
+ * Entity status types.
32
+ */
33
+ var EntityStatus;
34
+ (function (EntityStatus) {
35
+ EntityStatus["ACTIVE"] = "active";
36
+ EntityStatus["INACTIVE"] = "inactive";
37
+ EntityStatus["ARCHIVED"] = "archived";
38
+ EntityStatus["DELETED"] = "deleted";
39
+ })(EntityStatus || (exports.EntityStatus = EntityStatus = {}));
40
+ /**
41
+ * Entity schema definition.
42
+ */
43
+ exports.entitySchema = zod_1.z.object({
44
+ id: zod_1.z.string().uuid().optional(),
45
+ refId: zod_1.z.string().optional(),
46
+ type: zod_1.z.string(),
47
+ name: zod_1.z.string(),
48
+ description: zod_1.z.string().optional(),
49
+ metadata: zod_1.z.record(zod_1.z.unknown()).default({}),
50
+ status: zod_1.z.nativeEnum(EntityStatus).default(EntityStatus.ACTIVE),
51
+ createdAt: zod_1.z.string().optional(),
52
+ updatedAt: zod_1.z.string().optional(),
53
+ });
54
+ /**
55
+ * Entity-memory link schema definition.
56
+ */
57
+ exports.entityMemoryLinkSchema = zod_1.z.object({
58
+ id: zod_1.z.string().uuid().optional(),
59
+ entityId: zod_1.z.string().uuid(),
60
+ memoryId: zod_1.z.string().uuid(),
61
+ relationship: zod_1.z.nativeEnum(EntityRelationshipType),
62
+ confidence: zod_1.z.number().min(0).max(1).default(1.0),
63
+ metadata: zod_1.z.record(zod_1.z.unknown()).default({}),
64
+ createdAt: zod_1.z.string().optional(),
65
+ });
18
66
  /**
19
67
  * Subtypes for activity data.
20
68
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@props-labs/mesh-os",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
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,20 @@
1
+ -- First drop the constraint since it depends on the function
2
+ ALTER TABLE public.agents DROP CONSTRAINT IF EXISTS valid_slug;
3
+
4
+ -- Now we can safely drop and recreate the function
5
+ DROP FUNCTION IF EXISTS validate_slug(text);
6
+ CREATE OR REPLACE FUNCTION validate_slug(slug text)
7
+ RETURNS boolean AS $$
8
+ BEGIN
9
+ RETURN slug ~ '^[a-z][a-z0-9_-]*[a-z0-9]$';
10
+ END;
11
+ $$ LANGUAGE plpgsql IMMUTABLE;
12
+
13
+ -- Update any existing invalid slugs to NULL
14
+ UPDATE public.agents
15
+ SET slug = NULL
16
+ WHERE slug IS NOT NULL AND NOT validate_slug(slug);
17
+
18
+ -- Finally add back the constraint
19
+ ALTER TABLE public.agents
20
+ ADD CONSTRAINT valid_slug CHECK (slug IS NULL OR validate_slug(slug));
@@ -0,0 +1,5 @@
1
+ -- Drop the valid_slug constraint from the agents table
2
+ ALTER TABLE public.agents DROP CONSTRAINT IF EXISTS valid_slug;
3
+
4
+ -- Drop the validate_slug function
5
+ DROP FUNCTION IF EXISTS validate_slug(text);
@@ -0,0 +1,13 @@
1
+ -- Drop the combined search function
2
+ DROP FUNCTION IF EXISTS search_memories_and_entities;
3
+
4
+ -- Drop the combined search results view
5
+ DROP VIEW IF EXISTS public.search_results_with_similarity;
6
+
7
+ -- Drop triggers
8
+ DROP TRIGGER IF EXISTS update_entities_updated_at ON public.entities;
9
+ DROP TRIGGER IF EXISTS normalize_entity_embedding ON public.entities;
10
+
11
+ -- Drop tables (order matters due to foreign key constraints)
12
+ DROP TABLE IF EXISTS public.entity_memory_links;
13
+ DROP TABLE IF EXISTS public.entities;
@@ -0,0 +1,155 @@
1
+ -- Create entities table with embedding support
2
+ CREATE TABLE IF NOT EXISTS public.entities (
3
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
4
+ ref_id TEXT UNIQUE,
5
+ type TEXT NOT NULL,
6
+ name TEXT NOT NULL,
7
+ description TEXT,
8
+ metadata JSONB,
9
+ embedding vector(1536),
10
+ status TEXT NOT NULL DEFAULT 'active',
11
+ created_at TIMESTAMPTZ DEFAULT NOW(),
12
+ updated_at TIMESTAMPTZ DEFAULT NOW()
13
+ );
14
+
15
+ -- Create entity-memory links table
16
+ CREATE TABLE IF NOT EXISTS public.entity_memory_links (
17
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
18
+ entity_id UUID NOT NULL REFERENCES public.entities(id) ON DELETE CASCADE,
19
+ memory_id UUID NOT NULL REFERENCES public.memories(id) ON DELETE CASCADE,
20
+ relationship TEXT NOT NULL,
21
+ confidence FLOAT DEFAULT 1.0,
22
+ metadata JSONB,
23
+ created_at TIMESTAMPTZ DEFAULT NOW(),
24
+ UNIQUE(entity_id, memory_id, relationship)
25
+ );
26
+
27
+ -- Create index for vector similarity search
28
+ CREATE INDEX IF NOT EXISTS idx_entities_embedding
29
+ ON public.entities USING ivfflat (embedding vector_cosine_ops)
30
+ WITH (lists = 100);
31
+
32
+ -- Create trigger for updated_at
33
+ CREATE TRIGGER update_entities_updated_at
34
+ BEFORE UPDATE ON public.entities
35
+ FOR EACH ROW
36
+ EXECUTE FUNCTION update_updated_at_column();
37
+
38
+ -- Add normalization trigger for entities
39
+ CREATE TRIGGER normalize_entity_embedding
40
+ BEFORE INSERT OR UPDATE OF embedding ON public.entities
41
+ FOR EACH ROW
42
+ EXECUTE FUNCTION normalize_embedding();
43
+
44
+ -- Create a view for combined search results
45
+ CREATE OR REPLACE VIEW public.search_results_with_similarity AS
46
+ SELECT
47
+ id,
48
+ 'memory'::text as type,
49
+ content,
50
+ metadata,
51
+ 0::float8 as similarity
52
+ FROM memories
53
+ UNION ALL
54
+ SELECT
55
+ id,
56
+ 'entity'::text as type,
57
+ name as content,
58
+ metadata,
59
+ 0::float8 as similarity
60
+ FROM entities;
61
+
62
+ -- Create combined search function
63
+ CREATE OR REPLACE FUNCTION search_memories_and_entities(
64
+ query_embedding vector(1536),
65
+ match_threshold float8,
66
+ match_count integer,
67
+ include_entities boolean DEFAULT true,
68
+ include_memories boolean DEFAULT true,
69
+ metadata_filter jsonb DEFAULT NULL,
70
+ created_at_filter jsonb DEFAULT NULL
71
+ )
72
+ RETURNS SETOF public.search_results_with_similarity
73
+ LANGUAGE sql STABLE AS $$
74
+ WITH normalized_query AS (
75
+ SELECT l2_normalize(query_embedding) AS normalized_vector
76
+ )
77
+ (
78
+ SELECT
79
+ m.id,
80
+ 'memory'::text as type,
81
+ m.content,
82
+ m.metadata,
83
+ -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) as similarity
84
+ FROM memories m
85
+ WHERE include_memories = true
86
+ AND CASE
87
+ WHEN metadata_filter IS NOT NULL THEN m.metadata @> metadata_filter
88
+ ELSE TRUE
89
+ END
90
+ AND CASE
91
+ WHEN created_at_filter IS NOT NULL THEN (
92
+ CASE
93
+ WHEN created_at_filter ? '_gt' THEN m.created_at > (created_at_filter->>'_gt')::timestamptz
94
+ ELSE TRUE
95
+ END
96
+ AND CASE
97
+ WHEN created_at_filter ? '_gte' THEN m.created_at >= (created_at_filter->>'_gte')::timestamptz
98
+ ELSE TRUE
99
+ END
100
+ AND CASE
101
+ WHEN created_at_filter ? '_lt' THEN m.created_at < (created_at_filter->>'_lt')::timestamptz
102
+ ELSE TRUE
103
+ END
104
+ AND CASE
105
+ WHEN created_at_filter ? '_lte' THEN m.created_at <= (created_at_filter->>'_lte')::timestamptz
106
+ ELSE TRUE
107
+ END
108
+ )
109
+ ELSE TRUE
110
+ END
111
+ AND -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) >= match_threshold
112
+ )
113
+ UNION ALL
114
+ (
115
+ SELECT
116
+ e.id,
117
+ 'entity'::text as type,
118
+ e.name as content,
119
+ e.metadata,
120
+ -(e.embedding <#> (SELECT normalized_vector FROM normalized_query)) as similarity
121
+ FROM entities e
122
+ WHERE include_entities = true
123
+ AND CASE
124
+ WHEN metadata_filter IS NOT NULL THEN e.metadata @> metadata_filter
125
+ ELSE TRUE
126
+ END
127
+ AND CASE
128
+ WHEN created_at_filter IS NOT NULL THEN (
129
+ CASE
130
+ WHEN created_at_filter ? '_gt' THEN e.created_at > (created_at_filter->>'_gt')::timestamptz
131
+ ELSE TRUE
132
+ END
133
+ AND CASE
134
+ WHEN created_at_filter ? '_gte' THEN e.created_at >= (created_at_filter->>'_gte')::timestamptz
135
+ ELSE TRUE
136
+ END
137
+ AND CASE
138
+ WHEN created_at_filter ? '_lt' THEN e.created_at < (created_at_filter->>'_lt')::timestamptz
139
+ ELSE TRUE
140
+ END
141
+ AND CASE
142
+ WHEN created_at_filter ? '_lte' THEN e.created_at <= (created_at_filter->>'_lte')::timestamptz
143
+ ELSE TRUE
144
+ END
145
+ )
146
+ ELSE TRUE
147
+ END
148
+ AND -(e.embedding <#> (SELECT normalized_vector FROM normalized_query)) >= match_threshold
149
+ )
150
+ ORDER BY similarity DESC
151
+ LIMIT match_count;
152
+ $$;
153
+
154
+ -- Track the function in Hasura
155
+ COMMENT ON FUNCTION search_memories_and_entities IS E'@graphql({"type": "Query"})';