@soulcraft/brainy 2.1.0 → 3.0.0

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.
Files changed (55) hide show
  1. package/dist/augmentations/AugmentationMetadataContract.d.ts +94 -0
  2. package/dist/augmentations/AugmentationMetadataContract.js +306 -0
  3. package/dist/augmentations/apiServerAugmentation.d.ts +1 -0
  4. package/dist/augmentations/apiServerAugmentation.js +1 -0
  5. package/dist/augmentations/batchProcessingAugmentation.d.ts +1 -0
  6. package/dist/augmentations/batchProcessingAugmentation.js +1 -0
  7. package/dist/augmentations/brainyAugmentation.d.ts +16 -0
  8. package/dist/augmentations/cacheAugmentation.d.ts +1 -0
  9. package/dist/augmentations/cacheAugmentation.js +1 -0
  10. package/dist/augmentations/conduitAugmentations.d.ts +1 -0
  11. package/dist/augmentations/conduitAugmentations.js +1 -0
  12. package/dist/augmentations/connectionPoolAugmentation.d.ts +1 -0
  13. package/dist/augmentations/connectionPoolAugmentation.js +1 -0
  14. package/dist/augmentations/entityRegistryAugmentation.d.ts +2 -0
  15. package/dist/augmentations/entityRegistryAugmentation.js +2 -0
  16. package/dist/augmentations/indexAugmentation.d.ts +1 -0
  17. package/dist/augmentations/indexAugmentation.js +1 -0
  18. package/dist/augmentations/intelligentVerbScoringAugmentation.d.ts +4 -0
  19. package/dist/augmentations/intelligentVerbScoringAugmentation.js +4 -0
  20. package/dist/augmentations/metadataEnforcer.d.ts +20 -0
  21. package/dist/augmentations/metadataEnforcer.js +171 -0
  22. package/dist/augmentations/metricsAugmentation.d.ts +2 -7
  23. package/dist/augmentations/metricsAugmentation.js +1 -0
  24. package/dist/augmentations/monitoringAugmentation.d.ts +1 -0
  25. package/dist/augmentations/monitoringAugmentation.js +1 -0
  26. package/dist/augmentations/neuralImport.d.ts +4 -0
  27. package/dist/augmentations/neuralImport.js +4 -0
  28. package/dist/augmentations/requestDeduplicatorAugmentation.d.ts +1 -0
  29. package/dist/augmentations/requestDeduplicatorAugmentation.js +1 -0
  30. package/dist/augmentations/serverSearchAugmentations.d.ts +2 -0
  31. package/dist/augmentations/serverSearchAugmentations.js +2 -0
  32. package/dist/augmentations/storageAugmentation.d.ts +1 -0
  33. package/dist/augmentations/storageAugmentation.js +1 -0
  34. package/dist/augmentations/synapseAugmentation.d.ts +4 -0
  35. package/dist/augmentations/synapseAugmentation.js +4 -0
  36. package/dist/augmentations/walAugmentation.d.ts +1 -0
  37. package/dist/augmentations/walAugmentation.js +1 -0
  38. package/dist/brainyData.d.ts +28 -1
  39. package/dist/brainyData.js +229 -83
  40. package/dist/embeddings/model-manager.d.ts +9 -8
  41. package/dist/embeddings/model-manager.js +105 -85
  42. package/dist/triple/TripleIntelligence.d.ts +4 -0
  43. package/dist/triple/TripleIntelligence.js +39 -9
  44. package/dist/utils/deletedItemsIndex.d.ts +59 -0
  45. package/dist/utils/deletedItemsIndex.js +98 -0
  46. package/dist/utils/ensureDeleted.d.ts +38 -0
  47. package/dist/utils/ensureDeleted.js +79 -0
  48. package/dist/utils/metadataFilter.js +5 -0
  49. package/dist/utils/metadataIndex.d.ts +4 -0
  50. package/dist/utils/metadataIndex.js +45 -0
  51. package/dist/utils/metadataNamespace.d.ts +113 -0
  52. package/dist/utils/metadataNamespace.js +162 -0
  53. package/dist/utils/periodicCleanup.d.ts +87 -0
  54. package/dist/utils/periodicCleanup.js +219 -0
  55. package/package.json +9 -3
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Runtime enforcement of metadata contracts
3
+ * Ensures augmentations only access declared fields
4
+ */
5
+ export class MetadataEnforcer {
6
+ /**
7
+ * Enforce metadata access based on augmentation contract
8
+ * Returns a wrapped metadata object that enforces the contract
9
+ */
10
+ static enforce(augmentation, metadata, operation = 'write') {
11
+ // Handle simple contracts
12
+ if (augmentation.metadata === 'none') {
13
+ // No access at all
14
+ if (operation === 'read')
15
+ return null;
16
+ throw new Error(`Augmentation '${augmentation.name}' has metadata='none' - cannot access metadata`);
17
+ }
18
+ if (augmentation.metadata === 'readonly') {
19
+ if (operation === 'read') {
20
+ // Return frozen deep clone for read-only access
21
+ return deepFreeze(deepClone(metadata));
22
+ }
23
+ throw new Error(`Augmentation '${augmentation.name}' has metadata='readonly' - cannot write`);
24
+ }
25
+ // Handle specific field access
26
+ const access = augmentation.metadata;
27
+ if (operation === 'read') {
28
+ // For reads, filter to allowed fields
29
+ if (access.reads === '*') {
30
+ return deepClone(metadata); // Can read everything
31
+ }
32
+ if (!access.reads) {
33
+ return {}; // No read access
34
+ }
35
+ // Filter to specific fields
36
+ const filtered = {};
37
+ for (const field of access.reads) {
38
+ if (field.includes('.')) {
39
+ // Handle nested fields like '_brainy.deleted'
40
+ const parts = field.split('.');
41
+ let source = metadata;
42
+ let target = filtered;
43
+ for (let i = 0; i < parts.length - 1; i++) {
44
+ const part = parts[i];
45
+ if (!source[part])
46
+ break;
47
+ if (!target[part])
48
+ target[part] = {};
49
+ source = source[part];
50
+ target = target[part];
51
+ }
52
+ const lastPart = parts[parts.length - 1];
53
+ if (source && lastPart in source) {
54
+ target[lastPart] = source[lastPart];
55
+ }
56
+ }
57
+ else {
58
+ // Simple field
59
+ if (field in metadata) {
60
+ filtered[field] = metadata[field];
61
+ }
62
+ }
63
+ }
64
+ return filtered;
65
+ }
66
+ // For writes, create a proxy that validates
67
+ return new Proxy(metadata, {
68
+ set(target, prop, value) {
69
+ const field = String(prop);
70
+ // Check if write is allowed
71
+ if (access.writes === '*') {
72
+ // Can write anything
73
+ target[prop] = value;
74
+ return true;
75
+ }
76
+ if (!access.writes || !access.writes.includes(field)) {
77
+ throw new Error(`Augmentation '${augmentation.name}' cannot write to field '${field}'. ` +
78
+ `Allowed writes: ${access.writes?.join(', ') || 'none'}`);
79
+ }
80
+ // Check namespace if specified
81
+ if (access.namespace && !field.startsWith(access.namespace)) {
82
+ console.warn(`Augmentation '${augmentation.name}' writing outside its namespace. ` +
83
+ `Expected: ${access.namespace}.*, got: ${field}`);
84
+ }
85
+ target[prop] = value;
86
+ return true;
87
+ },
88
+ deleteProperty(target, prop) {
89
+ const field = String(prop);
90
+ // Deletion counts as a write
91
+ if (access.writes === '*' || access.writes?.includes(field)) {
92
+ delete target[prop];
93
+ return true;
94
+ }
95
+ throw new Error(`Augmentation '${augmentation.name}' cannot delete field '${field}'`);
96
+ }
97
+ });
98
+ }
99
+ /**
100
+ * Validate that an augmentation's actual behavior matches its contract
101
+ * Used in testing to verify contracts are accurate
102
+ */
103
+ static async validateContract(augmentation, testMetadata = { test: 'data', _brainy: { deleted: false } }) {
104
+ const violations = [];
105
+ // Test read access
106
+ try {
107
+ const readable = this.enforce(augmentation, testMetadata, 'read');
108
+ if (augmentation.metadata === 'none' && readable !== null) {
109
+ violations.push(`Contract says 'none' but got readable metadata`);
110
+ }
111
+ }
112
+ catch (error) {
113
+ violations.push(`Read enforcement error: ${error}`);
114
+ }
115
+ // Test write access
116
+ try {
117
+ const writable = this.enforce(augmentation, testMetadata, 'write');
118
+ if (augmentation.metadata === 'none') {
119
+ violations.push(`Contract says 'none' but got writable metadata`);
120
+ }
121
+ if (augmentation.metadata === 'readonly') {
122
+ // Try to write - should fail
123
+ try {
124
+ writable.testWrite = 'value';
125
+ violations.push(`Contract says 'readonly' but write succeeded`);
126
+ }
127
+ catch {
128
+ // Expected to fail
129
+ }
130
+ }
131
+ }
132
+ catch (error) {
133
+ // Expected for 'none' and 'readonly' on write
134
+ if (augmentation.metadata !== 'none' && augmentation.metadata !== 'readonly') {
135
+ violations.push(`Write enforcement error: ${error}`);
136
+ }
137
+ }
138
+ return {
139
+ valid: violations.length === 0,
140
+ violations
141
+ };
142
+ }
143
+ }
144
+ // Helper functions
145
+ function deepClone(obj) {
146
+ if (obj === null || typeof obj !== 'object')
147
+ return obj;
148
+ if (obj instanceof Date)
149
+ return new Date(obj);
150
+ if (obj instanceof Array)
151
+ return obj.map(item => deepClone(item));
152
+ const cloned = {};
153
+ for (const key in obj) {
154
+ if (obj.hasOwnProperty(key)) {
155
+ cloned[key] = deepClone(obj[key]);
156
+ }
157
+ }
158
+ return cloned;
159
+ }
160
+ function deepFreeze(obj) {
161
+ Object.freeze(obj);
162
+ Object.getOwnPropertyNames(obj).forEach(prop => {
163
+ if (obj[prop] !== null &&
164
+ (typeof obj[prop] === 'object' || typeof obj[prop] === 'function') &&
165
+ !Object.isFrozen(obj[prop])) {
166
+ deepFreeze(obj[prop]);
167
+ }
168
+ });
169
+ return obj;
170
+ }
171
+ //# sourceMappingURL=metadataEnforcer.js.map
@@ -27,6 +27,7 @@ export interface MetricsConfig {
27
27
  * - Zero-config with smart defaults
28
28
  */
29
29
  export declare class MetricsAugmentation extends BaseAugmentation {
30
+ readonly metadata: "readonly";
30
31
  readonly name = "metrics";
31
32
  readonly timing: "after";
32
33
  operations: ("add" | "search" | "delete" | "clear" | "all")[];
@@ -126,13 +127,7 @@ export declare class MetricsAugmentation extends BaseAugmentation {
126
127
  averageSearchTimeMs: number;
127
128
  searchesLastHour: number;
128
129
  searchesLastDay: number;
129
- topSearchTerms
130
- /**
131
- * Update storage size metrics
132
- */
133
- ? /**
134
- * Update storage size metrics
135
- */: string[];
130
+ topSearchTerms?: string[];
136
131
  } | undefined;
137
132
  verbStatistics?: {
138
133
  totalVerbs: number;
@@ -21,6 +21,7 @@ import { StatisticsCollector } from '../utils/statisticsCollector.js';
21
21
  export class MetricsAugmentation extends BaseAugmentation {
22
22
  constructor(config = {}) {
23
23
  super();
24
+ this.metadata = 'readonly'; // Reads metadata for metrics
24
25
  this.name = 'metrics';
25
26
  this.timing = 'after';
26
27
  this.operations = ['add', 'search', 'delete', 'clear', 'all'];
@@ -28,6 +28,7 @@ export interface MonitoringConfig {
28
28
  * - Zero-config with smart defaults
29
29
  */
30
30
  export declare class MonitoringAugmentation extends BaseAugmentation {
31
+ readonly metadata: "readonly";
31
32
  readonly name = "monitoring";
32
33
  readonly timing: "after";
33
34
  operations: ("search" | "add" | "delete" | "all")[];
@@ -23,6 +23,7 @@ import { DistributedConfigManager as ConfigManager } from '../distributed/config
23
23
  export class MonitoringAugmentation extends BaseAugmentation {
24
24
  constructor(config = {}) {
25
25
  super();
26
+ this.metadata = 'readonly'; // Reads metadata for monitoring
26
27
  this.name = 'monitoring';
27
28
  this.timing = 'after';
28
29
  this.operations = ['search', 'add', 'delete', 'all'];
@@ -55,6 +55,10 @@ export interface NeuralImportConfig {
55
55
  export declare class NeuralImportAugmentation extends BaseAugmentation {
56
56
  readonly name = "neural-import";
57
57
  readonly timing: "before";
58
+ readonly metadata: {
59
+ reads: "*";
60
+ writes: string[];
61
+ };
58
62
  operations: ("add" | "addNoun" | "addVerb" | "all")[];
59
63
  readonly priority = 80;
60
64
  private config;
@@ -19,6 +19,10 @@ export class NeuralImportAugmentation extends BaseAugmentation {
19
19
  super();
20
20
  this.name = 'neural-import';
21
21
  this.timing = 'before'; // Process data before storage
22
+ this.metadata = {
23
+ reads: '*', // Needs to read data for analysis
24
+ writes: ['_neuralProcessed', '_neuralConfidence', '_detectedEntities', '_detectedRelationships', '_neuralInsights', 'nounType', 'verbType']
25
+ }; // Enriches metadata with neural analysis
22
26
  this.operations = ['add', 'addNoun', 'addVerb', 'all']; // Use 'all' to catch batch operations
23
27
  this.priority = 80; // High priority for data processing
24
28
  this.analysisCache = new Map();
@@ -13,6 +13,7 @@ interface DeduplicatorConfig {
13
13
  export declare class RequestDeduplicatorAugmentation extends BaseAugmentation {
14
14
  name: string;
15
15
  timing: "around";
16
+ metadata: "none";
16
17
  operations: ("search" | "searchText" | "searchByNounTypes" | "findSimilar" | "get")[];
17
18
  priority: number;
18
19
  private pendingRequests;
@@ -10,6 +10,7 @@ export class RequestDeduplicatorAugmentation extends BaseAugmentation {
10
10
  super();
11
11
  this.name = 'RequestDeduplicator';
12
12
  this.timing = 'around';
13
+ this.metadata = 'none'; // Doesn't access metadata
13
14
  this.operations = ['search', 'searchText', 'searchByNounTypes', 'findSimilar', 'get'];
14
15
  this.priority = 50; // Performance optimization
15
16
  this.pendingRequests = new Map();
@@ -16,6 +16,7 @@ import { BrainyDataInterface } from '../types/brainyDataInterface.js';
16
16
  export declare class ServerSearchConduitAugmentation extends BaseAugmentation {
17
17
  readonly name = "server-search-conduit";
18
18
  readonly timing: "after";
19
+ readonly metadata: "readonly";
19
20
  operations: ("addNoun" | "delete" | "addVerb")[];
20
21
  readonly priority = 20;
21
22
  private localDb;
@@ -101,6 +102,7 @@ export declare class ServerSearchConduitAugmentation extends BaseAugmentation {
101
102
  export declare class ServerSearchActivationAugmentation extends BaseAugmentation {
102
103
  readonly name = "server-search-activation";
103
104
  readonly timing: "after";
105
+ readonly metadata: "readonly";
104
106
  operations: ("search" | "addNoun")[];
105
107
  readonly priority = 20;
106
108
  private conduitAugmentation;
@@ -16,6 +16,7 @@ export class ServerSearchConduitAugmentation extends BaseAugmentation {
16
16
  super();
17
17
  this.name = 'server-search-conduit';
18
18
  this.timing = 'after';
19
+ this.metadata = 'readonly'; // Reads metadata to sync with server
19
20
  this.operations = ['addNoun', 'delete', 'addVerb'];
20
21
  this.priority = 20;
21
22
  this.localDb = null;
@@ -310,6 +311,7 @@ export class ServerSearchActivationAugmentation extends BaseAugmentation {
310
311
  super();
311
312
  this.name = 'server-search-activation';
312
313
  this.timing = 'after';
314
+ this.metadata = 'readonly'; // Reads metadata for server activation
313
315
  this.operations = ['search', 'addNoun'];
314
316
  this.priority = 20;
315
317
  this.conduitAugmentation = null;
@@ -12,6 +12,7 @@ import { StorageAdapter } from '../coreTypes.js';
12
12
  */
13
13
  export declare abstract class StorageAugmentation extends BaseAugmentation implements BrainyAugmentation {
14
14
  readonly timing: "replace";
15
+ readonly metadata: "none";
15
16
  operations: ("storage")[];
16
17
  readonly priority = 100;
17
18
  protected storageAdapter: StorageAdapter | null;
@@ -14,6 +14,7 @@ export class StorageAugmentation extends BaseAugmentation {
14
14
  constructor() {
15
15
  super();
16
16
  this.timing = 'replace';
17
+ this.metadata = 'none'; // Storage doesn't directly access metadata
17
18
  this.operations = ['storage']; // Make mutable for TypeScript compatibility
18
19
  this.priority = 100; // High priority for storage
19
20
  this.storageAdapter = null;
@@ -27,6 +27,10 @@ export declare abstract class SynapseAugmentation extends BaseAugmentation {
27
27
  readonly timing: "after";
28
28
  readonly operations: ("all")[];
29
29
  readonly priority = 10;
30
+ readonly metadata: {
31
+ reads: "*";
32
+ writes: string[];
33
+ };
30
34
  abstract readonly synapseId: string;
31
35
  abstract readonly supportedTypes: string[];
32
36
  protected syncInProgress: boolean;
@@ -29,6 +29,10 @@ export class SynapseAugmentation extends BaseAugmentation {
29
29
  this.timing = 'after';
30
30
  this.operations = ['all'];
31
31
  this.priority = 10;
32
+ this.metadata = {
33
+ reads: '*', // Needs to read for syncing
34
+ writes: ['_synapse', '_syncedAt']
35
+ }; // Adds synapse tracking metadata
32
36
  // State management
33
37
  this.syncInProgress = false;
34
38
  this.syncStats = {
@@ -24,6 +24,7 @@ interface WALConfig {
24
24
  export declare class WALAugmentation extends BaseAugmentation {
25
25
  name: string;
26
26
  timing: "around";
27
+ metadata: "readonly";
27
28
  operations: ("addNoun" | "addVerb" | "saveNoun" | "saveVerb" | "updateMetadata" | "delete" | "deleteVerb" | "clear")[];
28
29
  priority: number;
29
30
  private config;
@@ -17,6 +17,7 @@ export class WALAugmentation extends BaseAugmentation {
17
17
  super();
18
18
  this.name = 'WAL';
19
19
  this.timing = 'around';
20
+ this.metadata = 'readonly'; // Reads metadata for logging/recovery
20
21
  this.operations = ['addNoun', 'addVerb', 'saveNoun', 'saveVerb', 'updateMetadata', 'delete', 'deleteVerb', 'clear'];
21
22
  this.priority = 100; // Critical system operation - highest priority
22
23
  this.operationCounter = 0;
@@ -6,6 +6,7 @@ import { HNSWIndex } from './hnsw/hnswIndex.js';
6
6
  import { HNSWIndexOptimized, HNSWOptimizedConfig } from './hnsw/hnswIndexOptimized.js';
7
7
  import { DistanceFunction, GraphVerb, EmbeddingFunction, HNSWConfig, SearchResult, SearchCursor, PaginatedSearchResult, StorageAdapter, Vector, VectorDocument } from './coreTypes.js';
8
8
  import { MetadataIndexConfig } from './utils/metadataIndex.js';
9
+ import { CleanupConfig } from './utils/periodicCleanup.js';
9
10
  import { NounType, VerbType } from './types/graphTypes.js';
10
11
  import { WebSocketConnection } from './types/augmentations.js';
11
12
  import { BrainyDataInterface } from './types/brainyDataInterface.js';
@@ -393,6 +394,12 @@ export interface BrainyDataConfig {
393
394
  * Default: false (enabled automatically for distributed setups)
394
395
  */
395
396
  health?: boolean;
397
+ /**
398
+ * Periodic cleanup configuration for old soft-deleted items
399
+ * Automatically removes soft-deleted items after a specified age to prevent memory buildup
400
+ * Default: enabled with 1 hour max age and 15 minute cleanup interval
401
+ */
402
+ cleanup?: Partial<CleanupConfig>;
396
403
  }
397
404
  export declare class BrainyData<T = any> implements BrainyDataInterface<T> {
398
405
  hnswIndex: HNSWIndex | HNSWIndexOptimized;
@@ -426,6 +433,7 @@ export declare class BrainyData<T = any> implements BrainyDataInterface<T> {
426
433
  private _nlpProcessor?;
427
434
  private _importManager?;
428
435
  private cacheAutoConfigurator;
436
+ private periodicCleanup;
429
437
  private timeoutConfig;
430
438
  private retryConfig;
431
439
  private cacheConfig;
@@ -485,6 +493,11 @@ export declare class BrainyData<T = any> implements BrainyDataInterface<T> {
485
493
  * Phase 3 of two-phase initialization
486
494
  */
487
495
  private initializeAugmentations;
496
+ /**
497
+ * Initialize periodic cleanup system for old soft-deleted items
498
+ * SAFETY-CRITICAL: Coordinates with both HNSW and metadata indexes
499
+ */
500
+ private initializePeriodicCleanup;
488
501
  private checkReadOnly;
489
502
  /**
490
503
  * Check if the database is frozen and throw an error if it is
@@ -1035,7 +1048,15 @@ export declare class BrainyData<T = any> implements BrainyDataInterface<T> {
1035
1048
  deleteVerbs(ids: string[]): Promise<boolean[]>;
1036
1049
  deleteVerb(id: string, options?: {
1037
1050
  service?: string;
1038
- hard?: boolean;
1051
+ }): Promise<boolean>;
1052
+ /**
1053
+ * Restore a soft-deleted verb (complement to consistent soft delete)
1054
+ * @param id The verb ID to restore
1055
+ * @param options Options for the restore operation
1056
+ * @returns Promise<boolean> True if restored, false if not found or not deleted
1057
+ */
1058
+ restoreVerb(id: string, options?: {
1059
+ service?: string;
1039
1060
  }): Promise<boolean>;
1040
1061
  /**
1041
1062
  * Get the number of vectors in the database
@@ -1630,6 +1651,12 @@ export declare class BrainyData<T = any> implements BrainyDataInterface<T> {
1630
1651
  * @returns Success boolean
1631
1652
  */
1632
1653
  deleteNoun(id: string): Promise<boolean>;
1654
+ /**
1655
+ * Restore a soft-deleted noun (complement to consistent soft delete)
1656
+ * @param id The noun ID to restore
1657
+ * @returns Promise<boolean> True if restored, false if not found or not deleted
1658
+ */
1659
+ restoreNoun(id: string): Promise<boolean>;
1633
1660
  /**
1634
1661
  * Delete multiple nouns by IDs
1635
1662
  * @param ids Array of noun IDs