@lov3kaizen/agentsea-memory 0.5.1

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 (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +450 -0
  3. package/dist/chunk-GACX3FPR.js +1402 -0
  4. package/dist/chunk-M44NB53O.js +1226 -0
  5. package/dist/chunk-MQDWBPZU.js +972 -0
  6. package/dist/chunk-TPC7MYWK.js +1495 -0
  7. package/dist/chunk-XD2CQGSD.js +1540 -0
  8. package/dist/chunk-YI7RPDEV.js +1215 -0
  9. package/dist/core.types-lkxKv-bW.d.cts +242 -0
  10. package/dist/core.types-lkxKv-bW.d.ts +242 -0
  11. package/dist/debug/index.cjs +1248 -0
  12. package/dist/debug/index.d.cts +3 -0
  13. package/dist/debug/index.d.ts +3 -0
  14. package/dist/debug/index.js +20 -0
  15. package/dist/index-7SsAJ4et.d.ts +525 -0
  16. package/dist/index-BGxYqpFb.d.cts +601 -0
  17. package/dist/index-BX62efZu.d.ts +565 -0
  18. package/dist/index-Bbc3COw0.d.cts +748 -0
  19. package/dist/index-Bczz1Eyk.d.ts +637 -0
  20. package/dist/index-C7pEiT8L.d.cts +637 -0
  21. package/dist/index-CHetLTb0.d.ts +389 -0
  22. package/dist/index-CloeiFyx.d.ts +748 -0
  23. package/dist/index-DNOhq-3y.d.cts +525 -0
  24. package/dist/index-Da-M8FOV.d.cts +389 -0
  25. package/dist/index-Dy8UjRFz.d.cts +565 -0
  26. package/dist/index-aVcITW0B.d.ts +601 -0
  27. package/dist/index.cjs +8554 -0
  28. package/dist/index.d.cts +293 -0
  29. package/dist/index.d.ts +293 -0
  30. package/dist/index.js +742 -0
  31. package/dist/processing/index.cjs +1575 -0
  32. package/dist/processing/index.d.cts +2 -0
  33. package/dist/processing/index.d.ts +2 -0
  34. package/dist/processing/index.js +24 -0
  35. package/dist/retrieval/index.cjs +1262 -0
  36. package/dist/retrieval/index.d.cts +2 -0
  37. package/dist/retrieval/index.d.ts +2 -0
  38. package/dist/retrieval/index.js +26 -0
  39. package/dist/sharing/index.cjs +1003 -0
  40. package/dist/sharing/index.d.cts +3 -0
  41. package/dist/sharing/index.d.ts +3 -0
  42. package/dist/sharing/index.js +16 -0
  43. package/dist/stores/index.cjs +1445 -0
  44. package/dist/stores/index.d.cts +2 -0
  45. package/dist/stores/index.d.ts +2 -0
  46. package/dist/stores/index.js +20 -0
  47. package/dist/structures/index.cjs +1530 -0
  48. package/dist/structures/index.d.cts +3 -0
  49. package/dist/structures/index.d.ts +3 -0
  50. package/dist/structures/index.js +24 -0
  51. package/package.json +141 -0
@@ -0,0 +1,972 @@
1
+ // src/sharing/SharedMemory.ts
2
+ import { EventEmitter } from "eventemitter3";
3
+ var SharedMemory = class extends EventEmitter {
4
+ store;
5
+ config;
6
+ sharedState = /* @__PURE__ */ new Map();
7
+ agentStates = /* @__PURE__ */ new Map();
8
+ subscriptions = /* @__PURE__ */ new Map();
9
+ // key -> agent IDs
10
+ lockManager = /* @__PURE__ */ new Map();
11
+ constructor(store, config = {}) {
12
+ super();
13
+ this.store = store;
14
+ this.config = {
15
+ conflictResolution: config.conflictResolution ?? "last-write-wins",
16
+ syncInterval: config.syncInterval ?? 5e3,
17
+ enableLocking: config.enableLocking ?? true,
18
+ lockTimeout: config.lockTimeout ?? 3e4,
19
+ maxSharedEntries: config.maxSharedEntries ?? 1e3
20
+ };
21
+ }
22
+ /**
23
+ * Set a shared value
24
+ */
25
+ async set(agentId, key, value) {
26
+ if (this.config.enableLocking && this.isLocked(key) && !this.hasLock(key, agentId)) {
27
+ return false;
28
+ }
29
+ const existing = this.sharedState.get(key);
30
+ if (existing && existing.writtenBy !== agentId) {
31
+ const resolved = this.resolveConflict(key, existing, { agentId, value });
32
+ if (!resolved) {
33
+ this.emit("conflict", key, [existing.writtenBy, agentId]);
34
+ return false;
35
+ }
36
+ }
37
+ const sharedValue = {
38
+ value,
39
+ writtenBy: agentId,
40
+ writtenAt: Date.now(),
41
+ version: (existing?.version ?? 0) + 1,
42
+ readers: existing?.readers ?? /* @__PURE__ */ new Set()
43
+ };
44
+ this.sharedState.set(key, sharedValue);
45
+ await this.persistSharedValue(key, sharedValue);
46
+ this.emit("write", agentId, key, value);
47
+ this.notifySubscribers(key, agentId, value);
48
+ return true;
49
+ }
50
+ /**
51
+ * Get a shared value
52
+ */
53
+ async get(agentId, key) {
54
+ const shared = this.sharedState.get(key);
55
+ if (!shared) {
56
+ const loaded = await this.loadSharedValue(key);
57
+ if (!loaded) return void 0;
58
+ this.sharedState.set(key, loaded);
59
+ return loaded.value;
60
+ }
61
+ shared.readers.add(agentId);
62
+ this.emit("read", agentId, key);
63
+ return shared.value;
64
+ }
65
+ /**
66
+ * Delete a shared value
67
+ */
68
+ async delete(agentId, key) {
69
+ if (this.config.enableLocking && this.isLocked(key) && !this.hasLock(key, agentId)) {
70
+ return false;
71
+ }
72
+ const existed = this.sharedState.delete(key);
73
+ if (existed) {
74
+ await this.store.delete(`shared:${key}`);
75
+ this.emit("delete", agentId, key);
76
+ }
77
+ return existed;
78
+ }
79
+ /**
80
+ * Set agent-specific state (not shared)
81
+ */
82
+ setAgentState(agentId, key, value) {
83
+ if (!this.agentStates.has(agentId)) {
84
+ this.agentStates.set(agentId, /* @__PURE__ */ new Map());
85
+ }
86
+ this.agentStates.get(agentId).set(key, value);
87
+ }
88
+ /**
89
+ * Get agent-specific state
90
+ */
91
+ getAgentState(agentId, key) {
92
+ return this.agentStates.get(agentId)?.get(key);
93
+ }
94
+ /**
95
+ * Get all agent state
96
+ */
97
+ getAllAgentState(agentId) {
98
+ return new Map(this.agentStates.get(agentId) ?? []);
99
+ }
100
+ /**
101
+ * Subscribe to changes on a key
102
+ */
103
+ subscribe(agentId, key) {
104
+ if (!this.subscriptions.has(key)) {
105
+ this.subscriptions.set(key, /* @__PURE__ */ new Set());
106
+ }
107
+ this.subscriptions.get(key).add(agentId);
108
+ }
109
+ /**
110
+ * Unsubscribe from changes
111
+ */
112
+ unsubscribe(agentId, key) {
113
+ this.subscriptions.get(key)?.delete(agentId);
114
+ }
115
+ /**
116
+ * Acquire a lock on a key
117
+ */
118
+ acquireLock(agentId, key) {
119
+ if (!this.config.enableLocking) return true;
120
+ const lock = this.lockManager.get(key);
121
+ if (lock && lock.expiresAt > Date.now() && lock.agentId !== agentId) {
122
+ return false;
123
+ }
124
+ this.lockManager.set(key, {
125
+ agentId,
126
+ expiresAt: Date.now() + this.config.lockTimeout
127
+ });
128
+ return true;
129
+ }
130
+ /**
131
+ * Release a lock
132
+ */
133
+ releaseLock(agentId, key) {
134
+ const lock = this.lockManager.get(key);
135
+ if (lock?.agentId === agentId) {
136
+ this.lockManager.delete(key);
137
+ return true;
138
+ }
139
+ return false;
140
+ }
141
+ /**
142
+ * Check if key is locked
143
+ */
144
+ isLocked(key) {
145
+ const lock = this.lockManager.get(key);
146
+ return lock !== void 0 && lock.expiresAt > Date.now();
147
+ }
148
+ /**
149
+ * Check if agent has lock
150
+ */
151
+ hasLock(key, agentId) {
152
+ const lock = this.lockManager.get(key);
153
+ return lock?.agentId === agentId && lock.expiresAt > Date.now();
154
+ }
155
+ /**
156
+ * Share memories from one agent to shared space
157
+ */
158
+ async shareMemories(agentId, entries) {
159
+ let added = 0;
160
+ let updated = 0;
161
+ let conflicts = 0;
162
+ for (const entry of entries) {
163
+ const key = `memory:${entry.id}`;
164
+ const existing = this.sharedState.get(key);
165
+ if (existing) {
166
+ if (existing.writtenBy !== agentId) {
167
+ if (this.config.conflictResolution === "last-write-wins") {
168
+ await this.set(agentId, key, entry);
169
+ updated++;
170
+ } else {
171
+ conflicts++;
172
+ }
173
+ } else {
174
+ await this.set(agentId, key, entry);
175
+ updated++;
176
+ }
177
+ } else {
178
+ await this.set(agentId, key, entry);
179
+ added++;
180
+ }
181
+ }
182
+ this.emit("sync", agentId, entries);
183
+ return {
184
+ added,
185
+ updated,
186
+ conflicts,
187
+ timestamp: Date.now()
188
+ };
189
+ }
190
+ /**
191
+ * Get shared memories
192
+ */
193
+ getSharedMemories(_agentId) {
194
+ const memories = [];
195
+ for (const [key, shared] of this.sharedState) {
196
+ if (key.startsWith("memory:")) {
197
+ const entry = shared.value;
198
+ memories.push(entry);
199
+ }
200
+ }
201
+ return memories;
202
+ }
203
+ /**
204
+ * Sync with store
205
+ */
206
+ async sync() {
207
+ for (const [key, value] of this.sharedState) {
208
+ await this.persistSharedValue(key, value);
209
+ }
210
+ }
211
+ /**
212
+ * Get all shared keys
213
+ */
214
+ getSharedKeys() {
215
+ return Array.from(this.sharedState.keys());
216
+ }
217
+ /**
218
+ * Get metadata for a shared value
219
+ */
220
+ getMetadata(key) {
221
+ const shared = this.sharedState.get(key);
222
+ if (!shared) return void 0;
223
+ return {
224
+ writtenBy: shared.writtenBy,
225
+ writtenAt: shared.writtenAt,
226
+ version: shared.version,
227
+ readers: shared.readers
228
+ };
229
+ }
230
+ /**
231
+ * Resolve conflict between values
232
+ */
233
+ resolveConflict(key, existing, incoming) {
234
+ switch (this.config.conflictResolution) {
235
+ case "last-write-wins":
236
+ return true;
237
+ case "first-write-wins":
238
+ return false;
239
+ case "merge":
240
+ if (typeof existing.value === "object" && typeof incoming.value === "object") {
241
+ this.sharedState.set(key, {
242
+ ...existing,
243
+ value: {
244
+ ...existing.value,
245
+ ...incoming.value
246
+ },
247
+ writtenBy: incoming.agentId,
248
+ writtenAt: Date.now(),
249
+ version: existing.version + 1
250
+ });
251
+ return true;
252
+ }
253
+ return true;
254
+ // Fall back to last-write-wins for non-objects
255
+ default:
256
+ return true;
257
+ }
258
+ }
259
+ /**
260
+ * Notify subscribers of changes
261
+ */
262
+ notifySubscribers(key, writerId, value) {
263
+ const subscribers = this.subscriptions.get(key);
264
+ if (!subscribers) return;
265
+ for (const agentId of subscribers) {
266
+ if (agentId !== writerId) {
267
+ this.emit("write", writerId, key, value);
268
+ }
269
+ }
270
+ }
271
+ /**
272
+ * Persist shared value to store
273
+ */
274
+ async persistSharedValue(key, value) {
275
+ await this.store.add({
276
+ id: `shared:${key}`,
277
+ content: JSON.stringify(value),
278
+ type: "context",
279
+ importance: 0.5,
280
+ metadata: {
281
+ source: "agent",
282
+ confidence: 1,
283
+ sharedKey: key,
284
+ writtenBy: value.writtenBy,
285
+ version: value.version
286
+ },
287
+ timestamp: value.writtenAt,
288
+ accessCount: 0,
289
+ createdAt: value.writtenAt,
290
+ updatedAt: value.writtenAt
291
+ });
292
+ }
293
+ /**
294
+ * Load shared value from store
295
+ */
296
+ async loadSharedValue(key) {
297
+ const entry = await this.store.get(`shared:${key}`);
298
+ if (!entry) return null;
299
+ try {
300
+ const data = JSON.parse(entry.content);
301
+ return {
302
+ ...data,
303
+ readers: new Set(data.readers ?? [])
304
+ };
305
+ } catch {
306
+ return null;
307
+ }
308
+ }
309
+ /**
310
+ * Get statistics
311
+ */
312
+ getStats() {
313
+ let totalSubscriptions = 0;
314
+ for (const subs of this.subscriptions.values()) {
315
+ totalSubscriptions += subs.size;
316
+ }
317
+ return {
318
+ sharedEntries: this.sharedState.size,
319
+ agentCount: this.agentStates.size,
320
+ activeLocks: Array.from(this.lockManager.values()).filter(
321
+ (l) => l.expiresAt > Date.now()
322
+ ).length,
323
+ totalSubscriptions
324
+ };
325
+ }
326
+ };
327
+ function createSharedMemory(store, config) {
328
+ return new SharedMemory(store, config);
329
+ }
330
+
331
+ // src/sharing/Namespaces.ts
332
+ import { EventEmitter as EventEmitter2 } from "eventemitter3";
333
+ var NamespaceManager = class extends EventEmitter2 {
334
+ store;
335
+ namespaces = /* @__PURE__ */ new Map();
336
+ defaultNamespace = "default";
337
+ constructor(store, _config) {
338
+ super();
339
+ this.store = store;
340
+ this.createNamespace("default", {
341
+ description: "Default namespace",
342
+ settings: { accessLevel: "public" }
343
+ });
344
+ }
345
+ /**
346
+ * Create a new namespace
347
+ */
348
+ createNamespace(name, options = {}) {
349
+ if (this.namespaces.has(name)) {
350
+ throw new Error(`Namespace "${name}" already exists`);
351
+ }
352
+ const metadata = {
353
+ name,
354
+ description: options.description,
355
+ owner: options.owner,
356
+ createdAt: Date.now(),
357
+ updatedAt: Date.now(),
358
+ entryCount: 0,
359
+ tags: options.tags,
360
+ settings: {
361
+ accessLevel: "private",
362
+ ...options.settings
363
+ }
364
+ };
365
+ this.namespaces.set(name, metadata);
366
+ this.emit("created", metadata);
367
+ return metadata;
368
+ }
369
+ /**
370
+ * Delete a namespace
371
+ */
372
+ async deleteNamespace(name, deleteEntries = false) {
373
+ if (name === "default") {
374
+ throw new Error("Cannot delete default namespace");
375
+ }
376
+ if (!this.namespaces.has(name)) {
377
+ return false;
378
+ }
379
+ if (deleteEntries) {
380
+ await this.store.clear({ namespace: name });
381
+ }
382
+ this.namespaces.delete(name);
383
+ this.emit("deleted", name);
384
+ return true;
385
+ }
386
+ /**
387
+ * Get namespace metadata
388
+ */
389
+ getNamespace(name) {
390
+ return this.namespaces.get(name);
391
+ }
392
+ /**
393
+ * List all namespaces
394
+ */
395
+ listNamespaces() {
396
+ return Array.from(this.namespaces.values());
397
+ }
398
+ /**
399
+ * Update namespace settings
400
+ */
401
+ updateNamespace(name, updates) {
402
+ const existing = this.namespaces.get(name);
403
+ if (!existing) return null;
404
+ const updated = {
405
+ ...existing,
406
+ ...updates,
407
+ settings: {
408
+ ...existing.settings,
409
+ ...updates.settings
410
+ },
411
+ updatedAt: Date.now()
412
+ };
413
+ this.namespaces.set(name, updated);
414
+ this.emit("updated", updated);
415
+ return updated;
416
+ }
417
+ /**
418
+ * Check if agent can access namespace
419
+ */
420
+ canAccess(name, agentId, action = "read") {
421
+ const namespace = this.namespaces.get(name);
422
+ if (!namespace) return false;
423
+ const { settings } = namespace;
424
+ if (settings.accessLevel === "public") {
425
+ return action === "read" || !settings.readOnly;
426
+ }
427
+ if (settings.accessLevel === "private") {
428
+ return namespace.owner === agentId;
429
+ }
430
+ if (settings.accessLevel === "restricted") {
431
+ const isAllowed = settings.allowedAgents?.includes(agentId) ?? false;
432
+ if (!isAllowed) {
433
+ this.emit("accessDenied", name, agentId, action);
434
+ return false;
435
+ }
436
+ return action === "read" || !settings.readOnly;
437
+ }
438
+ return false;
439
+ }
440
+ /**
441
+ * Add entry to namespace
442
+ */
443
+ async addEntry(namespace, entry, agentId) {
444
+ if (!this.canAccess(namespace, agentId, "write")) {
445
+ return null;
446
+ }
447
+ const ns = this.namespaces.get(namespace);
448
+ if (!ns) return null;
449
+ if (ns.settings.maxEntries && ns.entryCount >= ns.settings.maxEntries) {
450
+ return null;
451
+ }
452
+ const fullEntry = {
453
+ ...entry,
454
+ metadata: {
455
+ source: entry.metadata?.source ?? "explicit",
456
+ confidence: entry.metadata?.confidence ?? 1,
457
+ ...entry.metadata,
458
+ namespace
459
+ },
460
+ expiresAt: ns.settings.ttl ? Date.now() + ns.settings.ttl : entry.expiresAt
461
+ };
462
+ await this.store.add(fullEntry);
463
+ ns.entryCount++;
464
+ ns.updatedAt = Date.now();
465
+ this.emit("entryAdded", namespace, fullEntry);
466
+ return fullEntry.id;
467
+ }
468
+ /**
469
+ * Query entries in namespace
470
+ */
471
+ async queryEntries(namespace, agentId, options) {
472
+ if (!this.canAccess(namespace, agentId, "read")) {
473
+ return [];
474
+ }
475
+ const { entries } = await this.store.query({
476
+ namespace,
477
+ query: options?.query,
478
+ limit: options?.limit,
479
+ types: options?.types
480
+ });
481
+ return entries;
482
+ }
483
+ /**
484
+ * Delete entry from namespace
485
+ */
486
+ async deleteEntry(namespace, entryId, agentId) {
487
+ if (!this.canAccess(namespace, agentId, "write")) {
488
+ return false;
489
+ }
490
+ const result = await this.store.delete(entryId);
491
+ if (result) {
492
+ const ns = this.namespaces.get(namespace);
493
+ if (ns) {
494
+ ns.entryCount = Math.max(0, ns.entryCount - 1);
495
+ ns.updatedAt = Date.now();
496
+ }
497
+ }
498
+ return result;
499
+ }
500
+ /**
501
+ * Move entries between namespaces
502
+ */
503
+ async moveEntries(fromNamespace, toNamespace, entryIds, agentId) {
504
+ if (!this.canAccess(fromNamespace, agentId, "write")) return 0;
505
+ if (!this.canAccess(toNamespace, agentId, "write")) return 0;
506
+ let moved = 0;
507
+ for (const id of entryIds) {
508
+ const entry = await this.store.get(id);
509
+ if (entry && entry.metadata.namespace === fromNamespace) {
510
+ await this.store.update(id, {
511
+ metadata: { ...entry.metadata, namespace: toNamespace }
512
+ });
513
+ moved++;
514
+ }
515
+ }
516
+ const fromNs = this.namespaces.get(fromNamespace);
517
+ const toNs = this.namespaces.get(toNamespace);
518
+ if (fromNs) fromNs.entryCount -= moved;
519
+ if (toNs) toNs.entryCount += moved;
520
+ return moved;
521
+ }
522
+ /**
523
+ * Copy entries between namespaces
524
+ */
525
+ async copyEntries(fromNamespace, toNamespace, entryIds, agentId) {
526
+ if (!this.canAccess(fromNamespace, agentId, "read")) return 0;
527
+ if (!this.canAccess(toNamespace, agentId, "write")) return 0;
528
+ let copied = 0;
529
+ for (const id of entryIds) {
530
+ const entry = await this.store.get(id);
531
+ if (entry && entry.metadata.namespace === fromNamespace) {
532
+ const newEntry = {
533
+ ...entry,
534
+ id: `${entry.id}-copy-${Date.now()}`,
535
+ metadata: { ...entry.metadata, namespace: toNamespace },
536
+ createdAt: Date.now(),
537
+ updatedAt: Date.now()
538
+ };
539
+ await this.store.add(newEntry);
540
+ copied++;
541
+ }
542
+ }
543
+ const toNs = this.namespaces.get(toNamespace);
544
+ if (toNs) toNs.entryCount += copied;
545
+ return copied;
546
+ }
547
+ /**
548
+ * Grant access to namespace
549
+ */
550
+ grantAccess(namespace, agentId) {
551
+ const ns = this.namespaces.get(namespace);
552
+ if (!ns) return false;
553
+ if (!ns.settings.allowedAgents) {
554
+ ns.settings.allowedAgents = [];
555
+ }
556
+ if (!ns.settings.allowedAgents.includes(agentId)) {
557
+ ns.settings.allowedAgents.push(agentId);
558
+ }
559
+ return true;
560
+ }
561
+ /**
562
+ * Revoke access from namespace
563
+ */
564
+ revokeAccess(namespace, agentId) {
565
+ const ns = this.namespaces.get(namespace);
566
+ if (!ns || !ns.settings.allowedAgents) return false;
567
+ const index = ns.settings.allowedAgents.indexOf(agentId);
568
+ if (index !== -1) {
569
+ ns.settings.allowedAgents.splice(index, 1);
570
+ return true;
571
+ }
572
+ return false;
573
+ }
574
+ /**
575
+ * Set default namespace
576
+ */
577
+ setDefaultNamespace(name) {
578
+ if (!this.namespaces.has(name)) return false;
579
+ this.defaultNamespace = name;
580
+ return true;
581
+ }
582
+ /**
583
+ * Get default namespace
584
+ */
585
+ getDefaultNamespace() {
586
+ return this.defaultNamespace;
587
+ }
588
+ /**
589
+ * Get namespace statistics
590
+ */
591
+ async getNamespaceStats(name) {
592
+ if (!this.namespaces.has(name)) return null;
593
+ const { entries, total } = await this.store.query({
594
+ namespace: name,
595
+ limit: 1e4
596
+ });
597
+ const typeDistribution = {};
598
+ let oldestEntry = null;
599
+ let newestEntry = null;
600
+ let totalSize = 0;
601
+ for (const entry of entries) {
602
+ typeDistribution[entry.type] = (typeDistribution[entry.type] ?? 0) + 1;
603
+ totalSize += entry.content.length;
604
+ if (oldestEntry === null || entry.timestamp < oldestEntry) {
605
+ oldestEntry = entry.timestamp;
606
+ }
607
+ if (newestEntry === null || entry.timestamp > newestEntry) {
608
+ newestEntry = entry.timestamp;
609
+ }
610
+ }
611
+ return {
612
+ entryCount: total,
613
+ totalSize,
614
+ oldestEntry,
615
+ newestEntry,
616
+ typeDistribution
617
+ };
618
+ }
619
+ };
620
+ function createNamespaceManager(store, config) {
621
+ return new NamespaceManager(store, config);
622
+ }
623
+
624
+ // src/sharing/AccessControl.ts
625
+ import { EventEmitter as EventEmitter3 } from "eventemitter3";
626
+ var AccessControl = class extends EventEmitter3 {
627
+ config;
628
+ rules = /* @__PURE__ */ new Map();
629
+ accessLog = [];
630
+ maxLogSize = 1e3;
631
+ constructor(config = {}) {
632
+ super();
633
+ this.config = {
634
+ roles: config.roles ?? {},
635
+ defaultRole: config.defaultRole ?? "user",
636
+ adminUsers: config.adminUsers ?? [],
637
+ defaultPermission: config.defaultPermission ?? "read",
638
+ enableAuditLog: config.enableAuditLog ?? true,
639
+ strictMode: config.strictMode ?? false,
640
+ maxRulesPerAgent: config.maxRulesPerAgent ?? 100
641
+ };
642
+ if (!this.config.strictMode) {
643
+ this.addRule({
644
+ id: "default-read",
645
+ agentId: "*",
646
+ resource: "*",
647
+ permission: this.config.defaultPermission,
648
+ createdBy: "system",
649
+ createdAt: Date.now()
650
+ });
651
+ }
652
+ }
653
+ /**
654
+ * Add a permission rule
655
+ */
656
+ addRule(rule) {
657
+ if (rule.agentId !== "*") {
658
+ const agentRules = Array.from(this.rules.values()).filter(
659
+ (r) => r.agentId === rule.agentId
660
+ );
661
+ if (agentRules.length >= this.config.maxRulesPerAgent) {
662
+ return false;
663
+ }
664
+ }
665
+ this.rules.set(rule.id, rule);
666
+ this.emit("ruleAdded", rule);
667
+ return true;
668
+ }
669
+ /**
670
+ * Remove a permission rule
671
+ */
672
+ removeRule(ruleId) {
673
+ const existed = this.rules.delete(ruleId);
674
+ if (existed) {
675
+ this.emit("ruleRemoved", ruleId);
676
+ }
677
+ return existed;
678
+ }
679
+ /**
680
+ * Get rule by ID
681
+ */
682
+ getRule(ruleId) {
683
+ return this.rules.get(ruleId);
684
+ }
685
+ /**
686
+ * Get all rules for an agent
687
+ */
688
+ getAgentRules(agentId) {
689
+ return Array.from(this.rules.values()).filter(
690
+ (r) => r.agentId === agentId || r.agentId === "*"
691
+ );
692
+ }
693
+ /**
694
+ * Check if access is allowed
695
+ */
696
+ checkAccess(request) {
697
+ const applicableRules = this.findApplicableRules(request);
698
+ if (applicableRules.length === 0) {
699
+ const result2 = {
700
+ allowed: !this.config.strictMode,
701
+ reason: this.config.strictMode ? "No applicable rules found" : "Default permission applied"
702
+ };
703
+ this.logAccess(request, result2);
704
+ return result2;
705
+ }
706
+ const sortedRules = this.sortRulesBySpecificity(applicableRules);
707
+ const bestRule = sortedRules[0];
708
+ if (bestRule.conditions && !this.checkConditions(request, bestRule.conditions)) {
709
+ const result2 = {
710
+ allowed: false,
711
+ reason: "Conditions not met",
712
+ matchedRule: bestRule
713
+ };
714
+ this.logAccess(request, result2);
715
+ return result2;
716
+ }
717
+ const requiredLevel = this.actionToPermissionLevel(request.action);
718
+ const hasPermission = this.hasPermissionLevel(
719
+ bestRule.permission,
720
+ requiredLevel
721
+ );
722
+ const result = {
723
+ allowed: hasPermission,
724
+ reason: hasPermission ? "Permission granted" : "Insufficient permission level",
725
+ matchedRule: bestRule
726
+ };
727
+ this.logAccess(request, result);
728
+ if (hasPermission) {
729
+ this.emit("accessGranted", request);
730
+ } else {
731
+ this.emit("accessDenied", request, result.reason);
732
+ }
733
+ return result;
734
+ }
735
+ /**
736
+ * Grant permission to agent
737
+ */
738
+ grantPermission(granterId, agentId, resource, permission, options) {
739
+ const rule = {
740
+ id: `rule-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
741
+ agentId,
742
+ resource,
743
+ permission,
744
+ conditions: options?.conditions,
745
+ expiresAt: options?.expiresAt,
746
+ createdBy: granterId,
747
+ createdAt: Date.now()
748
+ };
749
+ this.addRule(rule);
750
+ return rule;
751
+ }
752
+ /**
753
+ * Revoke all permissions for agent on resource
754
+ */
755
+ revokePermission(agentId, resource) {
756
+ const toRemove = [];
757
+ for (const [id, rule] of this.rules) {
758
+ if (rule.agentId === agentId && rule.resource === resource) {
759
+ toRemove.push(id);
760
+ }
761
+ }
762
+ for (const id of toRemove) {
763
+ this.removeRule(id);
764
+ }
765
+ return toRemove.length;
766
+ }
767
+ /**
768
+ * Check if agent has specific permission
769
+ */
770
+ hasPermission(agentId, resource, action) {
771
+ return this.checkAccess({ agentId, resource, action }).allowed;
772
+ }
773
+ /**
774
+ * Get access log
775
+ */
776
+ getAccessLog(options) {
777
+ let log = [...this.accessLog];
778
+ if (options?.agentId) {
779
+ log = log.filter((e) => e.agentId === options.agentId);
780
+ }
781
+ if (options?.resource) {
782
+ log = log.filter((e) => e.resource === options.resource);
783
+ }
784
+ if (options?.startTime) {
785
+ log = log.filter((e) => e.timestamp >= options.startTime);
786
+ }
787
+ if (options?.endTime) {
788
+ log = log.filter((e) => e.timestamp <= options.endTime);
789
+ }
790
+ log.sort((a, b) => b.timestamp - a.timestamp);
791
+ return options?.limit ? log.slice(0, options.limit) : log;
792
+ }
793
+ /**
794
+ * Clear access log
795
+ */
796
+ clearAccessLog() {
797
+ this.accessLog = [];
798
+ }
799
+ /**
800
+ * Find applicable rules for request
801
+ */
802
+ findApplicableRules(request) {
803
+ const now = Date.now();
804
+ return Array.from(this.rules.values()).filter((rule) => {
805
+ if (rule.expiresAt && rule.expiresAt < now) {
806
+ return false;
807
+ }
808
+ if (rule.agentId !== "*" && rule.agentId !== request.agentId) {
809
+ return false;
810
+ }
811
+ if (rule.resource !== "*" && rule.resource !== request.resource) {
812
+ if (!request.resource.startsWith(rule.resource + ":")) {
813
+ return false;
814
+ }
815
+ }
816
+ return true;
817
+ });
818
+ }
819
+ /**
820
+ * Sort rules by specificity (most specific first)
821
+ */
822
+ sortRulesBySpecificity(rules) {
823
+ return rules.sort((a, b) => {
824
+ const agentSpecA = a.agentId !== "*" ? 1 : 0;
825
+ const agentSpecB = b.agentId !== "*" ? 1 : 0;
826
+ if (agentSpecA !== agentSpecB) return agentSpecB - agentSpecA;
827
+ const resourceSpecA = a.resource !== "*" ? 1 : 0;
828
+ const resourceSpecB = b.resource !== "*" ? 1 : 0;
829
+ if (resourceSpecA !== resourceSpecB) return resourceSpecB - resourceSpecA;
830
+ const levelA = this.permissionToLevel(a.permission);
831
+ const levelB = this.permissionToLevel(b.permission);
832
+ return levelB - levelA;
833
+ });
834
+ }
835
+ /**
836
+ * Check conditions
837
+ */
838
+ checkConditions(request, conditions) {
839
+ const now = Date.now();
840
+ for (const condition of conditions) {
841
+ switch (condition.type) {
842
+ case "time-range": {
843
+ const { start, end } = condition.value;
844
+ if (now < start || now > end) return false;
845
+ break;
846
+ }
847
+ case "entry-type": {
848
+ if (!request.entry) break;
849
+ const allowedTypes = condition.value;
850
+ if (!allowedTypes.includes(request.entry.type)) return false;
851
+ break;
852
+ }
853
+ case "importance": {
854
+ if (!request.entry) break;
855
+ const { min, max } = condition.value;
856
+ if (min !== void 0 && request.entry.importance < min) return false;
857
+ if (max !== void 0 && request.entry.importance > max) return false;
858
+ break;
859
+ }
860
+ case "custom": {
861
+ const fn = condition.value;
862
+ if (!fn(request)) return false;
863
+ break;
864
+ }
865
+ }
866
+ }
867
+ return true;
868
+ }
869
+ /**
870
+ * Convert action to required permission level
871
+ */
872
+ actionToPermissionLevel(action) {
873
+ switch (action) {
874
+ case "read":
875
+ return 1;
876
+ case "write":
877
+ return 2;
878
+ case "delete":
879
+ return 2;
880
+ case "admin":
881
+ return 3;
882
+ default:
883
+ return 1;
884
+ }
885
+ }
886
+ /**
887
+ * Convert permission to level number
888
+ */
889
+ permissionToLevel(permission) {
890
+ switch (permission) {
891
+ case "none":
892
+ return 0;
893
+ case "read":
894
+ return 1;
895
+ case "write":
896
+ return 2;
897
+ case "admin":
898
+ return 3;
899
+ default:
900
+ return 0;
901
+ }
902
+ }
903
+ /**
904
+ * Check if permission level is sufficient
905
+ */
906
+ hasPermissionLevel(permission, required) {
907
+ return this.permissionToLevel(permission) >= required;
908
+ }
909
+ /**
910
+ * Log access attempt
911
+ */
912
+ logAccess(request, result) {
913
+ if (!this.config.enableAuditLog) return;
914
+ this.accessLog.push({
915
+ timestamp: Date.now(),
916
+ agentId: request.agentId,
917
+ resource: request.resource,
918
+ action: request.action,
919
+ allowed: result.allowed,
920
+ reason: result.reason
921
+ });
922
+ if (this.accessLog.length > this.maxLogSize) {
923
+ this.accessLog = this.accessLog.slice(-this.maxLogSize);
924
+ }
925
+ }
926
+ /**
927
+ * Get statistics
928
+ */
929
+ getStats() {
930
+ const agents = new Set(
931
+ Array.from(this.rules.values()).filter((r) => r.agentId !== "*").map((r) => r.agentId)
932
+ );
933
+ const granted = this.accessLog.filter((e) => e.allowed).length;
934
+ const denied = this.accessLog.filter((e) => !e.allowed).length;
935
+ return {
936
+ totalRules: this.rules.size,
937
+ agentCount: agents.size,
938
+ accessGranted: granted,
939
+ accessDenied: denied
940
+ };
941
+ }
942
+ /**
943
+ * Export rules
944
+ */
945
+ exportRules() {
946
+ return Array.from(this.rules.values());
947
+ }
948
+ /**
949
+ * Import rules
950
+ */
951
+ importRules(rules) {
952
+ let imported = 0;
953
+ for (const rule of rules) {
954
+ if (this.addRule(rule)) {
955
+ imported++;
956
+ }
957
+ }
958
+ return imported;
959
+ }
960
+ };
961
+ function createAccessControl(config) {
962
+ return new AccessControl(config);
963
+ }
964
+
965
+ export {
966
+ SharedMemory,
967
+ createSharedMemory,
968
+ NamespaceManager,
969
+ createNamespaceManager,
970
+ AccessControl,
971
+ createAccessControl
972
+ };