lex-agentic-memory 0.1.26 → 0.1.28

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e847dc63590787f98f33e475b6f2b02efb6a8628be4ac4a2ecd3d9c80a7fe559
4
- data.tar.gz: e1428ea0a5d9b7c462ebe7d4e96a01fb137b7ab9e989a738d029cb8d529b6d70
3
+ metadata.gz: a4a726ce7792c2342e46df371973c90e78d11957ec56c76f4ab6c2f7c8c31b7a
4
+ data.tar.gz: 2fab727488f18d34881ff4805dcf5452b4a2fd3bde78a5924f5a638f4440b1c1
5
5
  SHA512:
6
- metadata.gz: aa1728b3ddf58527e4ed616258d1632a13c6c5bb8f501044f9a7ee1d1961cfd303f54767aaabd5ebd3bdc03c3472604229405b4cee4b4c067df6c075373e2d67
7
- data.tar.gz: db56e23e2c2effcd767e74d41eb3943431e2ff4054aa530d36800ab67fb6cbb88e2b80d84ebdab7180b2b5a3b0a107b5be7cc36725e7b018ea12413ed0f6d610
6
+ metadata.gz: ca13a314ea5c7eb0895bb98e36da27fd53992a3e66fd9b6e4da0e257a24e532fbd69be957a771d1f84295178b1e5b8fce20d18ec8a2289390474018714956e0d
7
+ data.tar.gz: ab7fe39c52cd26ac67567583f0f7c5ffa38cf16f8c0e8def6daeb3d3f93a22363dc0a8f130c8e777fa0545637ea234533a98a57f495cb1fd874b95767ed5e1de
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.28] - 2026-04-22
4
+ ### Fixed
5
+ - Snapshot save/restore now logs warnings when Self/Affect module methods are unavailable instead of silently skipping
6
+ - 26+ silent rescue blocks in trace persistence layer (postgres_store, store, hot_tier) now log errors via `log.error`
7
+ ### Changed
8
+ - CLAUDE.md updated to document all 16 actors (was 5) and CommunicationPattern sub-module
9
+
10
+ ## [0.1.27] - 2026-04-17
11
+ ### Fixed
12
+ - Add missing `Legion::Logging::Helper` include to `CacheStore` — resolves `NameError: undefined local variable or method 'log'` crash on startup when Memcached is available (#23)
13
+
3
14
  ## [0.1.26] - 2026-04-15
4
15
  ### Changed
5
16
  - Set `mcp_tools?`, `mcp_tools_deferred?`, and `transport_required?` to `false` — internal cognitive pipeline extension
data/README.md CHANGED
@@ -1,43 +1,57 @@
1
1
  # lex-agentic-memory
2
2
 
3
- Domain consolidation gem for memory storage, retrieval, and consolidation. Bundles 18 source extensions into one loadable unit under `Legion::Extensions::Agentic::Memory`.
3
+ Domain consolidation gem for memory storage, retrieval, and consolidation. Bundles 19 sub-modules into one loadable unit under `Legion::Extensions::Agentic::Memory`.
4
4
 
5
5
  ## Overview
6
6
 
7
7
  **Gem**: `lex-agentic-memory`
8
- **Version**: 0.1.26
8
+ **Version**: 0.1.28
9
9
  **Namespace**: `Legion::Extensions::Agentic::Memory`
10
10
 
11
11
  ## Sub-Modules
12
12
 
13
- | Sub-Module | Source Gem | Purpose |
14
- |---|---|---|
15
- | `Memory::Trace` | `lex-memory` | Memory trace storage, power-law decay, Hebbian association, tiered retrieval |
16
- | `Memory::Episodic` | `lex-episodic-buffer` | Baddeley & Hitch episodic buffer — integrates working memory channels |
17
- | `Memory::Semantic` | `lex-semantic-memory` | Long-term conceptual knowledge — spreading activation |
18
- | `Memory::SemanticPriming` | `lex-semantic-priming` | Prior exposure boosts retrieval speed for related concepts |
19
- | `Memory::SemanticSatiation` | `lex-semantic-satiation` | Repeated activation reduces salience — cognitive desensitization |
20
- | `Memory::SourceMonitoring` | `lex-source-monitoring` | Attribution of memories to origin source |
21
- | `Memory::Transfer` | `lex-transfer-learning` | Knowledge transfer between domains |
22
- | `Memory::Archaeology` | `lex-cognitive-archaeology` | Excavates dormant or deeply buried traces |
23
- | `Memory::Paleontology` | `lex-cognitive-paleontology` | Excavating old knowledge layers |
24
- | `Memory::Palimpsest` | `lex-cognitive-palimpsest` | Layered memory overwriting — recovering original layers |
25
- | `Memory::Compression` | `lex-cognitive-compression` | Memory compression for storage efficiency |
26
- | `Memory::Hologram` | `lex-cognitive-hologram` | Distributed memory storage with holographic properties |
27
- | `Memory::Offloading` | `lex-cognitive-offloading` | Externalizing memory to reduce cognitive load |
28
- | `Memory::Nostalgia` | `lex-cognitive-nostalgia` | Nostalgic retrieval bias — past warmth enhancement |
29
- | `Memory::Echo` | `lex-cognitive-echo` | Echo/resonance of past experiences |
30
- | `Memory::EchoChamber` | `lex-cognitive-echo-chamber` | Self-reinforcing memory patterns |
31
- | `Memory::ImmuneMemory` | `lex-cognitive-immune-memory` | Immune-style memory for threat patterns |
32
- | `Memory::Reserve` | `lex-cognitive-reserve` | Cognitive reserve capacity |
13
+ | Sub-Module | Purpose |
14
+ |---|---|
15
+ | `Memory::Trace` | Memory trace storage, power-law decay, Hebbian association, tiered retrieval |
16
+ | `Memory::Episodic` | Baddeley & Hitch episodic buffer — integrates working memory channels |
17
+ | `Memory::Semantic` | Long-term conceptual knowledge — spreading activation |
18
+ | `Memory::SemanticPriming` | Prior exposure boosts retrieval speed for related concepts |
19
+ | `Memory::SemanticSatiation` | Repeated activation reduces salience — cognitive desensitization |
20
+ | `Memory::SourceMonitoring` | Attribution of memories to origin source |
21
+ | `Memory::Transfer` | Knowledge transfer between domains |
22
+ | `Memory::Archaeology` | Excavates dormant or deeply buried traces |
23
+ | `Memory::Paleontology` | Excavating old knowledge layers |
24
+ | `Memory::Palimpsest` | Layered memory overwriting — recovering original layers |
25
+ | `Memory::Compression` | Memory compression for storage efficiency |
26
+ | `Memory::Hologram` | Distributed memory storage with holographic properties |
27
+ | `Memory::Offloading` | Externalizing memory to reduce cognitive load |
28
+ | `Memory::Nostalgia` | Nostalgic retrieval bias — past warmth enhancement |
29
+ | `Memory::Echo` | Echo/resonance of past experiences |
30
+ | `Memory::EchoChamber` | Self-reinforcing memory patterns |
31
+ | `Memory::ImmuneMemory` | Immune-style memory for threat patterns |
32
+ | `Memory::Reserve` | Cognitive reserve capacity |
33
+ | `Memory::CommunicationPattern` | Tracks temporal and channel communication patterns across traces; exposes `update_patterns`, `analyze_patterns`, `pattern_stats` |
33
34
 
34
35
  ## Actors
35
36
 
36
- - `Memory::Episodic::Actors::Decay` interval actor, decays episodic buffer entries
37
- - `Memory::Semantic::Actors::Decay` — interval actor, decays semantic memory activation
38
- - `Memory::SourceMonitoring::Actors::Decay` — interval actor, decays source monitoring confidence
39
- - `Memory::Trace::Actors::Decay` — runs every 60s, executes `decay_cycle`
40
- - `Memory::Trace::Actors::TierMigration` — runs every 300s, migrates traces between tiers
37
+ 16 actors handle autonomous background processing (all interval-based):
38
+
39
+ - `Memory::Archaeology::Actor::Decay` — every 120s
40
+ - `Memory::Compression::Actor::Maintenance` — every 300s
41
+ - `Memory::Echo::Actor::Decay` — every 60s
42
+ - `Memory::EchoChamber::Actor::Decay` — every 60s
43
+ - `Memory::Episodic::Actor::Decay` — every 15s
44
+ - `Memory::ImmuneMemory::Actor::Decay` — every 60s
45
+ - `Memory::Nostalgia::Actor::Maintenance` — every 120s
46
+ - `Memory::Palimpsest::Actor::Decay` — every 60s
47
+ - `Memory::Reserve::Actor::Maintenance` — every 60s
48
+ - `Memory::Semantic::Actor::Decay` — every 300s
49
+ - `Memory::SemanticPriming::Actor::Decay` — every 30s
50
+ - `Memory::SemanticSatiation::Actor::Recovery` — every 60s
51
+ - `Memory::SourceMonitoring::Actor::Decay` — every 60s
52
+ - `Memory::Trace::Actor::Decay` — every 60s
53
+ - `Memory::Trace::Actor::Quota` — every 300s
54
+ - `Memory::Trace::Actor::TierMigration` — every 300s
41
55
 
42
56
  ## Installation
43
57
 
@@ -49,8 +63,8 @@ gem 'lex-agentic-memory'
49
63
 
50
64
  ```bash
51
65
  bundle install
52
- bundle exec rspec # 1804 examples, 0 failures
53
- bundle exec rubocop # 0 offenses
66
+ bundle exec rspec
67
+ bundle exec rubocop
54
68
  ```
55
69
 
56
70
  ## License
@@ -11,6 +11,8 @@ module Legion
11
11
  # An index key tracks all known trace IDs.
12
12
  # Keeps a local in-memory copy for fast reads; syncs to cache on flush.
13
13
  class CacheStore
14
+ include Legion::Logging::Helper if defined?(Legion::Logging::Helper)
15
+
14
16
  TRACE_PREFIX = 'legion:memory:trace:'
15
17
  INDEX_KEY = 'legion:memory:trace_index'
16
18
  ASSOC_KEY = 'legion:memory:associations'
@@ -9,6 +9,10 @@ module Legion
9
9
  module HotTier
10
10
  HOT_TTL = 86_400 # 24 hours
11
11
 
12
+ def self.log
13
+ Legion::Logging
14
+ end
15
+
12
16
  module_function
13
17
 
14
18
  # Cache a trace in the Redis hot tier.
@@ -52,7 +56,8 @@ module Legion
52
56
  def available?
53
57
  defined?(Legion::Cache::RedisHash) &&
54
58
  Legion::Cache::RedisHash.redis_available?
55
- rescue StandardError => _e
59
+ rescue StandardError => e
60
+ log.error "[trace_persistence] hot_tier available?: #{e.message}"
56
61
  false
57
62
  end
58
63
 
@@ -12,6 +12,8 @@ module Legion
12
12
  # All writes go directly to the database — no in-memory dirty tracking, no flush.
13
13
  # Scoped by tenant_id and agent_id so multiple agents can share the same DB tables safely.
14
14
  class PostgresStore
15
+ include Legion::Logging::Helper if defined?(Legion::Logging::Helper)
16
+
15
17
  TRACES_TABLE = :memory_traces
16
18
  ASSOCIATIONS_TABLE = :memory_associations
17
19
 
@@ -267,7 +269,8 @@ module Legion
267
269
  Legion::Data.respond_to?(:connection) &&
268
270
  Legion::Data.connection&.table_exists?(TRACES_TABLE) &&
269
271
  Legion::Data.connection.table_exists?(ASSOCIATIONS_TABLE)
270
- rescue StandardError => _e
272
+ rescue StandardError => e
273
+ log.error "[trace_persistence] db_ready?: #{e.message}"
271
274
  false
272
275
  end
273
276
 
@@ -279,7 +282,8 @@ module Legion
279
282
 
280
283
  def resolve_agent_id
281
284
  Legion::Settings.dig(:agent, :id) || 'default'
282
- rescue StandardError => _e
285
+ rescue StandardError => e
286
+ log.error "[trace_persistence] resolve_agent_id: #{e.message}"
283
287
  'default'
284
288
  end
285
289
 
@@ -392,7 +396,8 @@ module Legion
392
396
 
393
397
  parsed = Legion::JSON.load(raw)
394
398
  parsed.is_a?(Hash) ? parsed : raw
395
- rescue StandardError => _e
399
+ rescue StandardError => e
400
+ log.error "[trace_persistence] parse_json_or_raw: #{e.message}"
396
401
  raw
397
402
  end
398
403
 
@@ -401,7 +406,8 @@ module Legion
401
406
 
402
407
  result = Legion::JSON.load(raw)
403
408
  result.is_a?(Array) ? result : []
404
- rescue StandardError => _e
409
+ rescue StandardError => e
410
+ log.error "[trace_persistence] parse_json_array: #{e.message}"
405
411
  []
406
412
  end
407
413
 
@@ -412,7 +418,7 @@ module Legion
412
418
  end
413
419
 
414
420
  def log_warn(message)
415
- Legion::Logging.warn "[memory:postgres_store] #{message}"
421
+ log.warn "[trace_persistence] #{message}"
416
422
  end
417
423
  end
418
424
  end
@@ -96,19 +96,31 @@ module Legion
96
96
  []
97
97
  end
98
98
 
99
+ # NOTE: lex-agentic-self must implement these module-level methods for
100
+ # personality and reflection state to be included in snapshots:
101
+ # Legion::Extensions::Agentic::Self.personality_snapshot => Hash
102
+ # Legion::Extensions::Agentic::Self.reflection_snapshot => Array
103
+ # Legion::Extensions::Agentic::Self.restore_personality(state)
104
+ # Legion::Extensions::Agentic::Self.restore_reflections(history)
99
105
  state[:personality_state] =
100
106
  if defined?(Legion::Extensions::Agentic::Self) &&
101
107
  Legion::Extensions::Agentic::Self.respond_to?(:personality_snapshot)
102
108
  Legion::Extensions::Agentic::Self.personality_snapshot
103
109
  else
110
+ log.warn '[snapshot] Legion::Extensions::Agentic::Self.personality_snapshot unavailable — personality state skipped'
104
111
  {}
105
112
  end
106
113
 
114
+ # NOTE: lex-agentic-affect must implement these module-level methods for
115
+ # mood state to be included in snapshots:
116
+ # Legion::Extensions::Agentic::Affect.mood_snapshot => Hash
117
+ # Legion::Extensions::Agentic::Affect.restore_mood(state)
107
118
  state[:mood_state] =
108
119
  if defined?(Legion::Extensions::Agentic::Affect) &&
109
120
  Legion::Extensions::Agentic::Affect.respond_to?(:mood_snapshot)
110
121
  Legion::Extensions::Agentic::Affect.mood_snapshot
111
122
  else
123
+ log.warn '[snapshot] Legion::Extensions::Agentic::Affect.mood_snapshot unavailable — mood state skipped'
112
124
  {}
113
125
  end
114
126
 
@@ -124,6 +136,7 @@ module Legion
124
136
  Legion::Extensions::Agentic::Self.respond_to?(:reflection_snapshot)
125
137
  Legion::Extensions::Agentic::Self.reflection_snapshot
126
138
  else
139
+ log.warn '[snapshot] Legion::Extensions::Agentic::Self.reflection_snapshot unavailable — reflection history skipped'
127
140
  []
128
141
  end
129
142
 
@@ -151,16 +164,24 @@ module Legion
151
164
 
152
165
  def restore_personality(personality_state)
153
166
  return unless personality_state && !personality_state.empty?
154
- return unless defined?(Legion::Extensions::Agentic::Self) &&
155
- Legion::Extensions::Agentic::Self.respond_to?(:restore_personality)
167
+
168
+ unless defined?(Legion::Extensions::Agentic::Self) &&
169
+ Legion::Extensions::Agentic::Self.respond_to?(:restore_personality)
170
+ log.warn '[snapshot] Legion::Extensions::Agentic::Self.restore_personality unavailable — personality state not restored'
171
+ return
172
+ end
156
173
 
157
174
  Legion::Extensions::Agentic::Self.restore_personality(personality_state)
158
175
  end
159
176
 
160
177
  def restore_mood(mood_state)
161
178
  return unless mood_state && !mood_state.empty?
162
- return unless defined?(Legion::Extensions::Agentic::Affect) &&
163
- Legion::Extensions::Agentic::Affect.respond_to?(:restore_mood)
179
+
180
+ unless defined?(Legion::Extensions::Agentic::Affect) &&
181
+ Legion::Extensions::Agentic::Affect.respond_to?(:restore_mood)
182
+ log.warn '[snapshot] Legion::Extensions::Agentic::Affect.restore_mood unavailable — mood state not restored'
183
+ return
184
+ end
164
185
 
165
186
  Legion::Extensions::Agentic::Affect.restore_mood(mood_state)
166
187
  end
@@ -174,8 +195,12 @@ module Legion
174
195
 
175
196
  def restore_reflections(reflection_history)
176
197
  return unless reflection_history && !reflection_history.empty?
177
- return unless defined?(Legion::Extensions::Agentic::Self) &&
178
- Legion::Extensions::Agentic::Self.respond_to?(:restore_reflections)
198
+
199
+ unless defined?(Legion::Extensions::Agentic::Self) &&
200
+ Legion::Extensions::Agentic::Self.respond_to?(:restore_reflections)
201
+ log.warn '[snapshot] Legion::Extensions::Agentic::Self.restore_reflections unavailable — reflection history not restored'
202
+ return
203
+ end
179
204
 
180
205
  Legion::Extensions::Agentic::Self.restore_reflections(reflection_history)
181
206
  end
@@ -11,6 +11,8 @@ module Legion
11
11
  # In-memory store for development and testing.
12
12
  # Production deployments should use a PostgreSQL + Redis backed store.
13
13
  class Store
14
+ include Legion::Logging::Helper if defined?(Legion::Logging::Helper)
15
+
14
16
  attr_reader :traces, :associations
15
17
 
16
18
  def initialize(partition_id: nil)
@@ -246,7 +248,8 @@ module Legion
246
248
 
247
249
  def resolve_partition_id
248
250
  Legion::Settings.dig(:agent, :id) || 'default'
249
- rescue StandardError => _e
251
+ rescue StandardError => e
252
+ log.error "[trace_persistence] resolve_partition_id: #{e.message}"
250
253
  'default'
251
254
  end
252
255
 
@@ -284,13 +287,7 @@ module Legion
284
287
  end
285
288
 
286
289
  def deserialize_trace_from_db(row)
287
- content_raw = row[:content]
288
- content = begin
289
- parsed = ::JSON.parse(content_raw, symbolize_names: true)
290
- parsed.is_a?(Hash) ? parsed : content_raw
291
- rescue StandardError => _e
292
- content_raw
293
- end
290
+ content = parse_db_content(row[:content])
294
291
  {
295
292
  trace_id: row[:trace_id],
296
293
  trace_type: row[:trace_type]&.to_sym,
@@ -299,17 +296,9 @@ module Legion
299
296
  strength: row[:strength],
300
297
  peak_strength: row[:peak_strength],
301
298
  base_decay_rate: row[:base_decay_rate],
302
- emotional_valence: begin
303
- ::JSON.parse(row[:emotional_valence], symbolize_names: true)
304
- rescue StandardError => _e
305
- 0.0
306
- end,
299
+ emotional_valence: parse_db_json(row[:emotional_valence], 'emotional_valence', symbolize: true) { 0.0 },
307
300
  emotional_intensity: row[:emotional_intensity],
308
- domain_tags: begin
309
- ::JSON.parse(row[:domain_tags])
310
- rescue StandardError => _e
311
- []
312
- end,
301
+ domain_tags: parse_db_json(row[:domain_tags], 'domain_tags') { [] },
313
302
  origin: row[:origin]&.to_sym,
314
303
  created_at: row[:created_at],
315
304
  last_reinforced: row[:last_reinforced],
@@ -318,22 +307,29 @@ module Legion
318
307
  confidence: row[:confidence],
319
308
  storage_tier: row[:storage_tier]&.to_sym,
320
309
  partition_id: row[:partition_id],
321
- associated_traces: begin
322
- ::JSON.parse(row[:associated_traces])
323
- rescue StandardError => _e
324
- []
325
- end,
310
+ associated_traces: parse_db_json(row[:associated_traces], 'associated_traces') { [] },
326
311
  parent_trace_id: row[:parent_id],
327
- child_trace_ids: begin
328
- ::JSON.parse(row[:child_ids])
329
- rescue StandardError => _e
330
- []
331
- end,
312
+ child_trace_ids: parse_db_json(row[:child_ids], 'child_ids') { [] },
332
313
  unresolved: row[:unresolved] || false,
333
314
  consolidation_candidate: row[:consolidation_candidate] || false
334
315
  }
335
316
  end
336
317
 
318
+ def parse_db_content(raw)
319
+ parsed = Legion::JSON.load(raw.to_s)
320
+ parsed.is_a?(Hash) ? parsed : raw
321
+ rescue StandardError => e
322
+ log.error "[trace_persistence] deserialize_trace_from_db content: #{e.message}"
323
+ raw
324
+ end
325
+
326
+ def parse_db_json(raw, field, **_opts, &default)
327
+ Legion::JSON.load(raw.to_s)
328
+ rescue StandardError => e
329
+ log.error "[trace_persistence] deserialize_trace_from_db #{field}: #{e.message}"
330
+ default&.call
331
+ end
332
+
337
333
  def link_traces(id_a, id_b)
338
334
  trace_a = @traces[id_a]
339
335
  trace_b = @traces[id_b]
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module Agentic
6
6
  module Memory
7
- VERSION = '0.1.26'
7
+ VERSION = '0.1.28'
8
8
  end
9
9
  end
10
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-agentic-memory
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.26
4
+ version: 0.1.28
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity