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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +43 -29
- data/lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb +2 -0
- data/lib/legion/extensions/agentic/memory/trace/helpers/hot_tier.rb +6 -1
- data/lib/legion/extensions/agentic/memory/trace/helpers/postgres_store.rb +11 -5
- data/lib/legion/extensions/agentic/memory/trace/helpers/snapshot.rb +31 -6
- data/lib/legion/extensions/agentic/memory/trace/helpers/store.rb +24 -28
- data/lib/legion/extensions/agentic/memory/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a4a726ce7792c2342e46df371973c90e78d11957ec56c76f4ab6c2f7c8c31b7a
|
|
4
|
+
data.tar.gz: 2fab727488f18d34881ff4805dcf5452b4a2fd3bde78a5924f5a638f4440b1c1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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.
|
|
8
|
+
**Version**: 0.1.28
|
|
9
9
|
**Namespace**: `Legion::Extensions::Agentic::Memory`
|
|
10
10
|
|
|
11
11
|
## Sub-Modules
|
|
12
12
|
|
|
13
|
-
| Sub-Module |
|
|
14
|
-
|
|
15
|
-
| `Memory::Trace` |
|
|
16
|
-
| `Memory::Episodic` |
|
|
17
|
-
| `Memory::Semantic` |
|
|
18
|
-
| `Memory::SemanticPriming` |
|
|
19
|
-
| `Memory::SemanticSatiation` |
|
|
20
|
-
| `Memory::SourceMonitoring` |
|
|
21
|
-
| `Memory::Transfer` |
|
|
22
|
-
| `Memory::Archaeology` |
|
|
23
|
-
| `Memory::Paleontology` |
|
|
24
|
-
| `Memory::Palimpsest` |
|
|
25
|
-
| `Memory::Compression` |
|
|
26
|
-
| `Memory::Hologram` |
|
|
27
|
-
| `Memory::Offloading` |
|
|
28
|
-
| `Memory::Nostalgia` |
|
|
29
|
-
| `Memory::Echo` |
|
|
30
|
-
| `Memory::EchoChamber` |
|
|
31
|
-
| `Memory::ImmuneMemory` |
|
|
32
|
-
| `Memory::Reserve` |
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
- `Memory::
|
|
39
|
-
- `Memory::
|
|
40
|
-
- `Memory::
|
|
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
|
|
53
|
-
bundle exec rubocop
|
|
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 =>
|
|
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 =>
|
|
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 =>
|
|
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 =>
|
|
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 =>
|
|
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
|
-
|
|
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
|
-
|
|
155
|
-
|
|
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
|
-
|
|
163
|
-
|
|
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
|
-
|
|
178
|
-
|
|
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 =>
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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]
|