lex-agentic-memory 0.1.8 → 0.1.11

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -1
  3. data/lib/legion/extensions/agentic/memory/archaeology/actors/decay.rb +22 -0
  4. data/lib/legion/extensions/agentic/memory/archaeology/runners/cognitive_archaeology.rb +6 -0
  5. data/lib/legion/extensions/agentic/memory/compression/actors/maintenance.rb +22 -0
  6. data/lib/legion/extensions/agentic/memory/echo/actors/decay.rb +22 -0
  7. data/lib/legion/extensions/agentic/memory/echo_chamber/actors/decay.rb +22 -0
  8. data/lib/legion/extensions/agentic/memory/echo_chamber/runners/cognitive_echo_chamber.rb +6 -0
  9. data/lib/legion/extensions/agentic/memory/immune_memory/actors/decay.rb +22 -0
  10. data/lib/legion/extensions/agentic/memory/nostalgia/actors/maintenance.rb +22 -0
  11. data/lib/legion/extensions/agentic/memory/palimpsest/actors/decay.rb +22 -0
  12. data/lib/legion/extensions/agentic/memory/reserve/actors/maintenance.rb +22 -0
  13. data/lib/legion/extensions/agentic/memory/semantic_priming/actors/decay.rb +22 -0
  14. data/lib/legion/extensions/agentic/memory/semantic_satiation/actors/recovery.rb +22 -0
  15. data/lib/legion/extensions/agentic/memory/trace/actors/quota.rb +22 -0
  16. data/lib/legion/extensions/agentic/memory/trace/helpers/hot_tier.rb +98 -0
  17. data/lib/legion/extensions/agentic/memory/trace/helpers/postgres_store.rb +393 -0
  18. data/lib/legion/extensions/agentic/memory/trace/runners/consolidation.rb +7 -0
  19. data/lib/legion/extensions/agentic/memory/trace.rb +22 -1
  20. data/lib/legion/extensions/agentic/memory/version.rb +1 -1
  21. data/spec/legion/extensions/agentic/memory/archaeology/actors/decay_spec.rb +24 -0
  22. data/spec/legion/extensions/agentic/memory/compression/actors/maintenance_spec.rb +24 -0
  23. data/spec/legion/extensions/agentic/memory/echo/actors/decay_spec.rb +24 -0
  24. data/spec/legion/extensions/agentic/memory/echo_chamber/actors/decay_spec.rb +24 -0
  25. data/spec/legion/extensions/agentic/memory/immune_memory/actors/decay_spec.rb +24 -0
  26. data/spec/legion/extensions/agentic/memory/nostalgia/actors/maintenance_spec.rb +24 -0
  27. data/spec/legion/extensions/agentic/memory/palimpsest/actors/decay_spec.rb +24 -0
  28. data/spec/legion/extensions/agentic/memory/reserve/actors/maintenance_spec.rb +24 -0
  29. data/spec/legion/extensions/agentic/memory/semantic_priming/actors/decay_spec.rb +24 -0
  30. data/spec/legion/extensions/agentic/memory/semantic_satiation/actors/recovery_spec.rb +24 -0
  31. data/spec/legion/extensions/agentic/memory/trace/actors/quota_spec.rb +24 -0
  32. data/spec/legion/extensions/agentic/memory/trace/helpers/hot_tier_spec.rb +337 -0
  33. data/spec/legion/extensions/agentic/memory/trace/helpers/postgres_store_spec.rb +464 -0
  34. metadata +27 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9df4d40acddf847a91c9b778461c54d9ce0353a071f54d232e12a249df899b01
4
- data.tar.gz: a41a803969657a0029fae7262f8889ecd2641483aed0dc628d3482990aa3c067
3
+ metadata.gz: 44a4999991835bdd28a7189717f6a53f38928644a86c7f653517331687659901
4
+ data.tar.gz: b3aeb50fddc4b82580430829ec924549690b8dd6171be74ff9213db40b135888
5
5
  SHA512:
6
- metadata.gz: c8e84baa2ab49fa7b8c79e6c0b572c5ab1f051b69ead4220435492e46a851b4052f67dd19b40ed3a0097c18895d9349212b34e9b50e3e66be00e6980d243b20a
7
- data.tar.gz: 66c47eb11ed41a51e284681d82c6f5936a88771fd218cc74ffeec913605ff9b07507af8522d490fa6eeeb4edb316d97c9f1bb157019bcca1801632e4f575c227
6
+ metadata.gz: 32930b8b8092925f0793eef8ae8b1a6764d50cbc9692bb2952c46fb969137b41609eed96ecb455fdc7d0518373ebd912075eff8ac47c4deb8e97e1fc9176962b
7
+ data.tar.gz: a142cbf70545c4f80717ddda3d81abab26a1cc2cf5d8e40584625a416629d1551991d716fc6a9592fff194b523b0ebed2963be24fa414039cf11bdb3c934e60a
data/CHANGELOG.md CHANGED
@@ -1,6 +1,29 @@
1
1
  # Changelog
2
2
 
3
- ## [Unreleased]
3
+ ## [0.1.11] - 2026-03-25
4
+
5
+ ### Added
6
+ - `Helpers::HotTier` module: Redis hot-tier cache in front of PostgresStore using `Legion::Cache::RedisHash`. Stores traces as Redis hashes with 24-hour TTL, maintains a sorted-set index per tenant, and provides `cache_trace`, `fetch_trace`, and `evict_trace` operations
7
+ - `PostgresStore#retrieve` checks hot tier first; falls through to DB on miss and populates hot tier on DB hit
8
+ - `PostgresStore#store` writes through to hot tier after successful DB write
9
+ - `PostgresStore#delete` evicts from hot tier before DB delete
10
+ - `PostgresStore#update` evicts stale hot-tier entry after DB update
11
+ - 32 new specs covering HotTier interface, serialize/deserialize round-trip, availability guard, and all four PostgresStore integration points
12
+
13
+ ## [0.1.10] - 2026-03-25
14
+
15
+ ### Added
16
+ - `Helpers::PostgresStore`: write-through durable store backed by Legion::Data (PostgreSQL or MySQL), scoped by tenant_id. Implements full store interface: store, retrieve, retrieve_by_type, retrieve_by_domain, all_traces, delete, update, record_coactivation, associations_for, walk_associations, delete_lowest_confidence, delete_least_recently_used, firmware_traces, flush (no-op), db_ready?
17
+ - `create_store` in `Trace` module now selects PostgresStore when a PostgreSQL or MySQL connection is available with both required tables; falls back to CacheStore or in-memory Store
18
+ - `postgres_available?` and `resolve_tenant_id` private helpers on `Trace` module
19
+ - 46 new specs covering all PostgresStore methods using an in-memory SQLite DB
20
+
21
+ ## [0.1.9] - 2026-03-25
22
+
23
+ ### Added
24
+ - Periodic actors for 10 sub-modules: Archaeology (decay), Compression (maintenance), Echo (decay), EchoChamber (decay), ImmuneMemory (decay), Nostalgia (maintenance), Palimpsest (decay), Reserve (maintenance), SemanticPriming (decay), SemanticSatiation (recovery) (closes #1)
25
+ - Quota enforcement actor for Trace (runs every 300s, calls enforce_quota) (closes #2)
26
+ - Runner methods: `decay_all` for Archaeology and EchoChamber, `enforce_quota` for Trace::Consolidation
4
27
 
5
28
  ### Fixed
6
29
  - Add Mutex synchronization to trace Store and CacheStore for thread safety during concurrent tick cycles
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module Archaeology
8
+ module Actors
9
+ class Decay < Legion::Extensions::Actors::Every
10
+ def runner_class = Runners::CognitiveArchaeology
11
+ def runner_function = 'decay_all'
12
+ def time = 120
13
+ def use_runner? = false
14
+ def check_subtask? = false
15
+ def generate_task? = false
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -53,6 +53,12 @@ module Legion
53
53
  count: results.size }
54
54
  end
55
55
 
56
+ def decay_all(rate: Helpers::Constants::PRESERVATION_DECAY, engine: nil, **)
57
+ eng = resolve_engine(engine)
58
+ eng.decay_all!(rate: rate)
59
+ { success: true, remaining: eng.all_artifacts.size }
60
+ end
61
+
56
62
  def archaeology_status(engine: nil, **)
57
63
  eng = resolve_engine(engine)
58
64
  { success: true, report: eng.archaeology_report }
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module Compression
8
+ module Actors
9
+ class Maintenance < Legion::Extensions::Actors::Every
10
+ def runner_class = Runners::CognitiveCompression
11
+ def runner_function = 'compress_all'
12
+ def time = 300
13
+ def use_runner? = false
14
+ def check_subtask? = false
15
+ def generate_task? = false
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module Echo
8
+ module Actors
9
+ class Decay < Legion::Extensions::Actors::Every
10
+ def runner_class = Runners::CognitiveEcho
11
+ def runner_function = 'decay_all'
12
+ def time = 60
13
+ def use_runner? = false
14
+ def check_subtask? = false
15
+ def generate_task? = false
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module EchoChamber
8
+ module Actors
9
+ class Decay < Legion::Extensions::Actors::Every
10
+ def runner_class = Runners::CognitiveEchoChamber
11
+ def runner_function = 'decay_all'
12
+ def time = 60
13
+ def use_runner? = false
14
+ def check_subtask? = false
15
+ def generate_task? = false
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -65,6 +65,12 @@ module Legion
65
65
  { success: true, echoes: echoes.map(&:to_h), count: echoes.size }
66
66
  end
67
67
 
68
+ def decay_all(engine: nil, **)
69
+ eng = engine || default_engine
70
+ eng.decay_all!
71
+ { success: true, **eng.echo_report }
72
+ end
73
+
68
74
  def chamber_status(engine: nil, **)
69
75
  eng = engine || default_engine
70
76
  { success: true, **eng.echo_report }
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module ImmuneMemory
8
+ module Actors
9
+ class Decay < Legion::Extensions::Actors::Every
10
+ def runner_class = Runners::CognitiveImmuneMemory
11
+ def runner_function = 'decay_all'
12
+ def time = 60
13
+ def use_runner? = false
14
+ def check_subtask? = false
15
+ def generate_task? = false
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module Nostalgia
8
+ module Actors
9
+ class Maintenance < Legion::Extensions::Actors::Every
10
+ def runner_class = Runners::Recall
11
+ def runner_function = 'age_memories'
12
+ def time = 120
13
+ def use_runner? = false
14
+ def check_subtask? = false
15
+ def generate_task? = false
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module Palimpsest
8
+ module Actors
9
+ class Decay < Legion::Extensions::Actors::Every
10
+ def runner_class = Runners::CognitivePalimpsest
11
+ def runner_function = 'decay_all_ghosts'
12
+ def time = 60
13
+ def use_runner? = false
14
+ def check_subtask? = false
15
+ def generate_task? = false
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module Reserve
8
+ module Actors
9
+ class Maintenance < Legion::Extensions::Actors::Every
10
+ def runner_class = Runners::CognitiveReserve
11
+ def runner_function = 'update_cognitive_reserve'
12
+ def time = 60
13
+ def use_runner? = false
14
+ def check_subtask? = false
15
+ def generate_task? = false
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module SemanticPriming
8
+ module Actors
9
+ class Decay < Legion::Extensions::Actors::Every
10
+ def runner_class = Runners::SemanticPriming
11
+ def runner_function = 'decay'
12
+ def time = 30
13
+ def use_runner? = false
14
+ def check_subtask? = false
15
+ def generate_task? = false
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module SemanticSatiation
8
+ module Actors
9
+ class Recovery < Legion::Extensions::Actors::Every
10
+ def runner_class = Runners::SemanticSatiation
11
+ def runner_function = 'recover'
12
+ def time = 60
13
+ def use_runner? = false
14
+ def check_subtask? = false
15
+ def generate_task? = false
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module Trace
8
+ module Actor
9
+ class Quota < Legion::Extensions::Actors::Every
10
+ def runner_class = Runners::Consolidation
11
+ def runner_function = 'enforce_quota'
12
+ def time = 300
13
+ def use_runner? = false
14
+ def check_subtask? = false
15
+ def generate_task? = false
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module Trace
8
+ module Helpers
9
+ module HotTier
10
+ HOT_TTL = 86_400 # 24 hours
11
+
12
+ module_function
13
+
14
+ # Cache a trace in the Redis hot tier.
15
+ def cache_trace(trace, tenant_id: nil)
16
+ return unless available?
17
+
18
+ tid = tenant_id || trace[:partition_id]
19
+ key = trace_key(tid, trace[:trace_id])
20
+ data = serialize_trace(trace)
21
+ Legion::Cache::RedisHash.hset(key, data)
22
+ Legion::Cache::RedisHash.expire(key, HOT_TTL)
23
+
24
+ index_key = "legion:tier:hot:#{tid}"
25
+ Legion::Cache::RedisHash.zadd(index_key, Time.now.to_f, trace[:trace_id])
26
+ end
27
+
28
+ # Fetch a trace from the hot tier. Returns a deserialized trace hash or nil on miss.
29
+ def fetch_trace(trace_id, tenant_id: nil)
30
+ return nil unless available?
31
+
32
+ key = trace_key(tenant_id, trace_id)
33
+ data = Legion::Cache::RedisHash.hgetall(key)
34
+ return nil if data.nil? || data.empty?
35
+
36
+ deserialize_trace(data)
37
+ end
38
+
39
+ # Evict a trace from the hot tier and remove it from the sorted-set index.
40
+ def evict_trace(trace_id, tenant_id: nil)
41
+ return unless available?
42
+
43
+ key = trace_key(tenant_id, trace_id)
44
+ Legion::Cache.delete(key)
45
+
46
+ index_key = "legion:tier:hot:#{tenant_id}"
47
+ Legion::Cache::RedisHash.zrem(index_key, trace_id)
48
+ end
49
+
50
+ # Returns true when the RedisHash module is loaded and Redis is reachable.
51
+ def available?
52
+ defined?(Legion::Cache::RedisHash) &&
53
+ Legion::Cache::RedisHash.redis_available?
54
+ rescue StandardError
55
+ false
56
+ end
57
+
58
+ # Build the namespaced Redis key for a trace.
59
+ def trace_key(tenant_id, trace_id)
60
+ "legion:trace:#{tenant_id}:#{trace_id}"
61
+ end
62
+
63
+ # Serialize a trace hash to a string-only flat hash suitable for Redis HSET.
64
+ def serialize_trace(trace)
65
+ {
66
+ 'trace_id' => trace[:trace_id].to_s,
67
+ 'trace_type' => trace[:trace_type].to_s,
68
+ 'content_payload' => trace[:content_payload].to_s,
69
+ 'strength' => trace[:strength].to_s,
70
+ 'peak_strength' => trace[:peak_strength].to_s,
71
+ 'confidence' => trace[:confidence].to_s,
72
+ 'storage_tier' => 'hot',
73
+ 'partition_id' => trace[:partition_id].to_s,
74
+ 'last_reinforced' => (trace[:last_reinforced] || Time.now).to_s
75
+ }
76
+ end
77
+
78
+ # Deserialize a Redis string-hash back to a typed trace hash.
79
+ def deserialize_trace(data)
80
+ {
81
+ trace_id: data['trace_id'],
82
+ trace_type: data['trace_type']&.to_sym,
83
+ content_payload: data['content_payload'],
84
+ strength: data['strength']&.to_f,
85
+ peak_strength: data['peak_strength']&.to_f,
86
+ confidence: data['confidence']&.to_f,
87
+ storage_tier: :hot,
88
+ partition_id: data['partition_id'],
89
+ last_reinforced: data['last_reinforced']
90
+ }
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end