llmemory 0.2.1 → 0.2.3

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +78 -1
  3. data/app/controllers/llmemory/dashboard/application_controller.rb +15 -1
  4. data/app/controllers/llmemory/dashboard/episodic_controller.rb +22 -0
  5. data/app/controllers/llmemory/dashboard/forget_log_controller.rb +12 -0
  6. data/app/controllers/llmemory/dashboard/maintenance_controller.rb +92 -0
  7. data/app/controllers/llmemory/dashboard/procedural_controller.rb +22 -0
  8. data/app/controllers/llmemory/dashboard/reflection_controller.rb +37 -0
  9. data/app/controllers/llmemory/dashboard/working_controller.rb +14 -0
  10. data/app/views/llmemory/dashboard/episodic/index.html.erb +37 -0
  11. data/app/views/llmemory/dashboard/forget_log/show.html.erb +23 -0
  12. data/app/views/llmemory/dashboard/maintenance/show.html.erb +65 -0
  13. data/app/views/llmemory/dashboard/procedural/index.html.erb +38 -0
  14. data/app/views/llmemory/dashboard/reflection/show.html.erb +29 -0
  15. data/app/views/llmemory/dashboard/users/show.html.erb +16 -0
  16. data/app/views/llmemory/dashboard/working/show.html.erb +20 -0
  17. data/config/routes.rb +14 -0
  18. data/lib/generators/llmemory/install/templates/create_llmemory_tables.rb +2 -0
  19. data/lib/llmemory/cli/commands/maintain.rb +62 -0
  20. data/lib/llmemory/cli/commands/mine_skills.rb +50 -0
  21. data/lib/llmemory/cli.rb +6 -0
  22. data/lib/llmemory/configuration.rb +28 -2
  23. data/lib/llmemory/crypto/cipher.rb +147 -0
  24. data/lib/llmemory/crypto/field_helpers.rb +110 -0
  25. data/lib/llmemory/instrumentation.rb +33 -0
  26. data/lib/llmemory/llm/anthropic.rb +21 -16
  27. data/lib/llmemory/llm/openai.rb +18 -13
  28. data/lib/llmemory/long_term/episodic/memory.rb +27 -13
  29. data/lib/llmemory/long_term/episodic/storage.rb +11 -4
  30. data/lib/llmemory/long_term/episodic/storages/active_record_storage.rb +33 -10
  31. data/lib/llmemory/long_term/episodic/storages/base.rb +15 -2
  32. data/lib/llmemory/long_term/episodic/storages/database_storage.rb +51 -8
  33. data/lib/llmemory/long_term/episodic/storages/file_storage.rb +47 -9
  34. data/lib/llmemory/long_term/episodic/storages/memory_storage.rb +35 -4
  35. data/lib/llmemory/long_term/file_based/memory.rb +12 -4
  36. data/lib/llmemory/long_term/file_based/storage.rb +11 -4
  37. data/lib/llmemory/long_term/file_based/storages/active_record_storage.rb +20 -12
  38. data/lib/llmemory/long_term/file_based/storages/base.rb +2 -2
  39. data/lib/llmemory/long_term/file_based/storages/database_storage.rb +28 -10
  40. data/lib/llmemory/long_term/file_based/storages/file_storage.rb +32 -16
  41. data/lib/llmemory/long_term/file_based/storages/memory_storage.rb +4 -2
  42. data/lib/llmemory/long_term/graph_based/memory.rb +16 -7
  43. data/lib/llmemory/long_term/graph_based/storage.rb +3 -2
  44. data/lib/llmemory/long_term/graph_based/storages/active_record_storage.rb +51 -23
  45. data/lib/llmemory/long_term/graph_based/storages/base.rb +2 -2
  46. data/lib/llmemory/long_term/graph_based/storages/memory_storage.rb +4 -2
  47. data/lib/llmemory/long_term/procedural/memory.rb +30 -16
  48. data/lib/llmemory/long_term/procedural/skill.rb +6 -2
  49. data/lib/llmemory/long_term/procedural/storage.rb +11 -4
  50. data/lib/llmemory/long_term/procedural/storages/active_record_storage.rb +47 -17
  51. data/lib/llmemory/long_term/procedural/storages/base.rb +14 -1
  52. data/lib/llmemory/long_term/procedural/storages/database_storage.rb +52 -10
  53. data/lib/llmemory/long_term/procedural/storages/file_storage.rb +49 -11
  54. data/lib/llmemory/long_term/procedural/storages/memory_storage.rb +36 -5
  55. data/lib/llmemory/maintenance/cognitive_pass.rb +109 -0
  56. data/lib/llmemory/maintenance/ttl_expiry.rb +50 -0
  57. data/lib/llmemory/maintenance.rb +2 -0
  58. data/lib/llmemory/mcp/server.rb +5 -1
  59. data/lib/llmemory/mcp/tools/memory_maintain.rb +53 -0
  60. data/lib/llmemory/mcp/tools/memory_mine_skills.rb +53 -0
  61. data/lib/llmemory/memory.rb +60 -8
  62. data/lib/llmemory/memory_module.rb +13 -6
  63. data/lib/llmemory/reflection/reflector.rb +24 -20
  64. data/lib/llmemory/retrieval/engine.rb +25 -16
  65. data/lib/llmemory/short_term/checkpoint.rb +3 -2
  66. data/lib/llmemory/short_term/stores/active_record_store.rb +12 -10
  67. data/lib/llmemory/short_term/stores/memory_store.rb +1 -1
  68. data/lib/llmemory/short_term/stores/postgres_store.rb +11 -5
  69. data/lib/llmemory/short_term/stores/redis_store.rb +7 -5
  70. data/lib/llmemory/short_term/stores.rb +7 -6
  71. data/lib/llmemory/skill_mining/miner.rb +163 -0
  72. data/lib/llmemory/skill_mining.rb +8 -0
  73. data/lib/llmemory/vector_store/active_record_store.rb +24 -3
  74. data/lib/llmemory/vector_store/memory_store.rb +23 -3
  75. data/lib/llmemory/vector_store/openai_embeddings.rb +11 -7
  76. data/lib/llmemory/vector_store.rb +4 -3
  77. data/lib/llmemory/version.rb +1 -1
  78. data/lib/llmemory.rb +4 -0
  79. metadata +24 -1
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Llmemory
6
+ module SkillMining
7
+ # Skill mining scans an agent's recent episodes (episodic memory) for
8
+ # repeated, successful trajectories and distills them into reusable skills
9
+ # (procedural memory). This is Voyager's actual contribution: rather than a
10
+ # passive, hand-written skill library, procedural memory grows from lived
11
+ # experience.
12
+ #
13
+ # Mining is human-in-the-loop by default: `mine` returns skill *proposals*
14
+ # and writes nothing. Pass `auto_register: true` to register them directly.
15
+ # Each registered skill carries provenance { method: "skill_mining",
16
+ # sources: [{ type: "episode", id: ... }] } so it stays traceable to the
17
+ # experiences it was distilled from.
18
+ #
19
+ # `procedural` must respond to:
20
+ # register_skill(name:, body:, description:, kind:, provenance:)
21
+ class Miner
22
+ DEFAULT_WINDOW = 20
23
+ DEFAULT_CONFIDENCE = 0.5
24
+ VALID_KINDS = %w[prompt template code].freeze
25
+
26
+ def initialize(episodic:, procedural:, llm: nil)
27
+ @episodic = episodic
28
+ @procedural = procedural
29
+ @llm = llm || Llmemory::LLM.client
30
+ end
31
+
32
+ # Mines the most recent `window` episodes for reusable skills. When
33
+ # `outcomes` (an allowlist of outcome labels) is given, only episodes whose
34
+ # outcome is in the set are considered — a deterministic pre-filter.
35
+ #
36
+ # Returns an array of proposal hashes
37
+ # ({ name:, kind:, body:, description:, confidence: }). When
38
+ # `auto_register: true`, registers each proposal and returns the new skill
39
+ # ids instead.
40
+ def mine(window: DEFAULT_WINDOW, outcomes: nil, auto_register: false)
41
+ result = []
42
+ Llmemory::Instrumentation.instrument(:mine_skills, window: window, auto_register: auto_register) do
43
+ episodes = @episodic.recent_episodes(limit: window)
44
+ episodes = filter_by_outcome(episodes, outcomes) if outcomes
45
+ next if episodes.empty?
46
+
47
+ proposals = distill(episodes)
48
+ next if proposals.empty?
49
+
50
+ result = auto_register ? register(proposals, episodes) : proposals
51
+ end
52
+ result
53
+ end
54
+
55
+ private
56
+
57
+ def filter_by_outcome(episodes, outcomes)
58
+ allowed = Array(outcomes).map { |o| o.to_s.strip.downcase }
59
+ episodes.select { |ep| allowed.include?(ep.outcome.to_s.strip.downcase) }
60
+ end
61
+
62
+ def register(proposals, episodes)
63
+ sources = episodes.map(&:id).compact.map { |id| { type: "episode", id: id } }
64
+ proposals.map do |p|
65
+ provenance = Llmemory::Provenance.build(
66
+ method: "skill_mining",
67
+ sources: sources,
68
+ confidence: p[:confidence]
69
+ )
70
+ @procedural.register_skill(
71
+ name: p[:name],
72
+ body: p[:body],
73
+ description: p[:description],
74
+ kind: p[:kind],
75
+ provenance: provenance
76
+ )
77
+ end
78
+ end
79
+
80
+ def distill(episodes)
81
+ response = @llm.invoke(build_prompt(episodes))
82
+ parse_proposals(response)
83
+ rescue Llmemory::LLMError
84
+ []
85
+ end
86
+
87
+ def build_prompt(episodes)
88
+ episodes_text = episodes.each_with_index.map do |ep, i|
89
+ "Episode #{i + 1} (outcome: #{ep.outcome || 'n/a'}):\n#{ep.searchable_text}"
90
+ end.join("\n\n")
91
+
92
+ <<~PROMPT
93
+ You are mining an agent's recent experiences for reusable skills. A skill
94
+ is a repeatable procedure the agent can apply again: a prompt, a template,
95
+ or a snippet of code. Only propose a skill when you see a SUCCESSFUL
96
+ pattern that recurs across episodes — generalize the steps into a reusable
97
+ procedure. Do not propose one-off actions or failures.
98
+
99
+ Recent episodes:
100
+ #{episodes_text}
101
+
102
+ Return a JSON array of objects with keys:
103
+ "name" (short snake_case identifier),
104
+ "kind" (one of "prompt", "template", "code"),
105
+ "body" (the reusable procedure itself),
106
+ "description" (one sentence on when to apply it),
107
+ "confidence" (0-1).
108
+ Return an empty array if no reusable skill can be distilled.
109
+ Example: [{"name": "rollback_on_deploy_failure", "kind": "prompt",
110
+ "body": "When a deploy fails, roll back to the last known-good release.",
111
+ "description": "Recover service after a failed deploy", "confidence": 0.8}]
112
+ PROMPT
113
+ end
114
+
115
+ def parse_proposals(response)
116
+ json = extract_json_array(response)
117
+ return [] unless json
118
+
119
+ json.filter_map do |item|
120
+ next nil unless item.is_a?(Hash)
121
+ name = (item["name"] || item[:name]).to_s.strip
122
+ body = (item["body"] || item[:body]).to_s.strip
123
+ next nil if name.empty? || body.empty?
124
+
125
+ {
126
+ name: name,
127
+ kind: normalize_kind(item["kind"] || item[:kind]),
128
+ body: body,
129
+ description: presence(item["description"] || item[:description]),
130
+ confidence: normalize_confidence(item["confidence"] || item[:confidence])
131
+ }
132
+ end
133
+ end
134
+
135
+ def normalize_kind(value)
136
+ k = value.to_s.strip.downcase
137
+ VALID_KINDS.include?(k) ? k : "prompt"
138
+ end
139
+
140
+ def normalize_confidence(value)
141
+ return DEFAULT_CONFIDENCE if value.nil?
142
+ v = value.to_f
143
+ v.between?(0, 1) ? v : DEFAULT_CONFIDENCE
144
+ end
145
+
146
+ def presence(value)
147
+ s = value.to_s.strip
148
+ s.empty? ? nil : s
149
+ end
150
+
151
+ def extract_json_array(response)
152
+ response = response.to_s.strip
153
+ start_idx = response.index("[")
154
+ end_idx = response.rindex("]")
155
+ return nil unless start_idx && end_idx
156
+
157
+ JSON.parse(response[start_idx..end_idx])
158
+ rescue JSON::ParserError
159
+ nil
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "skill_mining/miner"
4
+
5
+ module Llmemory
6
+ module SkillMining
7
+ end
8
+ end
@@ -7,10 +7,11 @@ module Llmemory
7
7
  # Persists embeddings in llmemory_embeddings (pgvector).
8
8
  # Use when long_term_store is :active_record so hybrid search finds persisted embeddings.
9
9
  class ActiveRecordStore < Base
10
- def initialize(embedding_provider: nil, source_type: "edge")
10
+ def initialize(embedding_provider: nil, source_type: "edge", cipher: nil)
11
11
  self.class.load_model!
12
12
  @embedding_provider = embedding_provider
13
13
  @source_type = source_type.to_s
14
+ @cipher = cipher || Llmemory.build_cipher
14
15
  end
15
16
 
16
17
  def self.load_model!
@@ -34,7 +35,7 @@ module Llmemory
34
35
  source_id: id.to_s
35
36
  )
36
37
  rec.embedding = embedding.to_a.map(&:to_f)
37
- rec.text_content = text_content
38
+ rec.text_content = encrypt_text_content(text_content)
38
39
  rec.save!
39
40
  id
40
41
  end
@@ -58,7 +59,11 @@ module Llmemory
58
59
  {
59
60
  id: r.source_id,
60
61
  score: score,
61
- metadata: { "text" => r.text_content, "created_at" => r.created_at, "user_id" => r.user_id }
62
+ metadata: {
63
+ "text" => decrypt_text_content(r.text_content),
64
+ "created_at" => r.created_at,
65
+ "user_id" => r.user_id
66
+ }
62
67
  }
63
68
  end
64
69
  end
@@ -69,6 +74,22 @@ module Llmemory
69
74
  query_embedding = @embedding_provider.embed(query_text)
70
75
  search(query_embedding, top_k: top_k, user_id: user_id)
71
76
  end
77
+
78
+ private
79
+
80
+ def encrypt_text_content(text)
81
+ return text if text.nil? || text.to_s.empty?
82
+ return text unless @cipher.enabled?
83
+
84
+ @cipher.encrypt(text.to_s)
85
+ end
86
+
87
+ def decrypt_text_content(text)
88
+ return text if text.nil?
89
+ return text unless text.is_a?(String) && @cipher.enabled? && @cipher.encrypted?(text)
90
+
91
+ @cipher.decrypt(text)
92
+ end
72
93
  end
73
94
  end
74
95
  end
@@ -5,9 +5,10 @@ require_relative "base"
5
5
  module Llmemory
6
6
  module VectorStore
7
7
  class MemoryStore < Base
8
- def initialize(embedding_provider: nil)
8
+ def initialize(embedding_provider: nil, cipher: nil)
9
9
  @entries = {}
10
10
  @embedding_provider = embedding_provider
11
+ @cipher = cipher || Llmemory.build_cipher
11
12
  end
12
13
 
13
14
  def embed(text)
@@ -17,7 +18,13 @@ module Llmemory
17
18
 
18
19
  def store(id:, embedding:, metadata: {}, user_id: nil)
19
20
  key = user_id ? "#{user_id}:#{id}" : id.to_s
20
- @entries[key] = { embedding: embedding.to_a.map(&:to_f), metadata: (metadata || {}).merge("user_id" => user_id) }
21
+ meta = (metadata || {}).dup
22
+ if meta["text"] && @cipher.enabled?
23
+ meta["text"] = @cipher.encrypt(meta["text"].to_s)
24
+ elsif meta[:text] && @cipher.enabled?
25
+ meta[:text] = @cipher.encrypt(meta[:text].to_s)
26
+ end
27
+ @entries[key] = { embedding: embedding.to_a.map(&:to_f), metadata: meta.merge("user_id" => user_id) }
21
28
  id
22
29
  end
23
30
 
@@ -27,7 +34,7 @@ module Llmemory
27
34
  entries = user_id ? @entries.select { |k, _| k.to_s.start_with?("#{user_id}:") } : @entries
28
35
  scores = entries.map do |id, data|
29
36
  sim = cosine_similarity(query, data[:embedding])
30
- { id: id, score: sim, metadata: data[:metadata] }
37
+ { id: id, score: sim, metadata: decrypt_metadata(data[:metadata]) }
31
38
  end
32
39
  scores.sort_by { |s| -s[:score] }.first(top_k)
33
40
  end
@@ -40,6 +47,19 @@ module Llmemory
40
47
 
41
48
  private
42
49
 
50
+ def decrypt_metadata(meta)
51
+ return meta unless meta.is_a?(Hash) && @cipher.enabled?
52
+
53
+ out = meta.dup
54
+ text = out["text"] || out[:text]
55
+ if text.is_a?(String) && @cipher.encrypted?(text)
56
+ decrypted = @cipher.decrypt(text)
57
+ out["text"] = decrypted
58
+ out[:text] = decrypted
59
+ end
60
+ out
61
+ end
62
+
43
63
  def cosine_similarity(a, b)
44
64
  return 0.0 if a.size != b.size || a.empty?
45
65
  dot = a.zip(b).sum { |x, y| x * y }
@@ -54,14 +54,18 @@ module Llmemory
54
54
  end
55
55
 
56
56
  def fetch_embedding(text)
57
- response = connection.post("embeddings") do |req|
58
- req.headers["Authorization"] = "Bearer #{@api_key}"
59
- req.headers["Content-Type"] = "application/json"
60
- req.body = { input: text.to_s.strip, model: @model }.to_json
57
+ result = nil
58
+ Llmemory::Instrumentation.instrument(:llm_embed, provider: :openai, model: @model, text_chars: text.to_s.length) do
59
+ response = connection.post("embeddings") do |req|
60
+ req.headers["Authorization"] = "Bearer #{@api_key}"
61
+ req.headers["Content-Type"] = "application/json"
62
+ req.body = { input: text.to_s.strip, model: @model }.to_json
63
+ end
64
+ raise Llmemory::LLMError, "OpenAI Embeddings API error: #{response.body}" unless response.success?
65
+ body = response.body.is_a?(Hash) ? response.body : JSON.parse(response.body.to_s)
66
+ result = body.dig("data", 0, "embedding")&.map(&:to_f) || Array.new(DEFAULT_DIMS, 0.0)
61
67
  end
62
- raise Llmemory::LLMError, "OpenAI Embeddings API error: #{response.body}" unless response.success?
63
- body = response.body.is_a?(Hash) ? response.body : JSON.parse(response.body.to_s)
64
- body.dig("data", 0, "embedding")&.map(&:to_f) || Array.new(DEFAULT_DIMS, 0.0)
68
+ result
65
69
  end
66
70
 
67
71
  def connection
@@ -10,14 +10,15 @@ module Llmemory
10
10
  # from config (:active_record persists in llmemory_embeddings; otherwise
11
11
  # in-process). `source_type` namespaces persisted embeddings so different
12
12
  # memory types (edges, episodes, skills) never collide in the shared table.
13
- def self.build(source_type: "edge")
13
+ def self.build(source_type: "edge", cipher: nil)
14
+ resolved_cipher = cipher || Llmemory.build_cipher
14
15
  embeddings = OpenAIEmbeddings.new
15
16
  store_type = (Llmemory.configuration.long_term_store || :memory).to_s.to_sym
16
17
  if store_type == :active_record || store_type == :activerecord
17
18
  require_relative "vector_store/active_record_store"
18
- ActiveRecordStore.new(embedding_provider: embeddings, source_type: source_type)
19
+ ActiveRecordStore.new(embedding_provider: embeddings, source_type: source_type, cipher: resolved_cipher)
19
20
  else
20
- MemoryStore.new(embedding_provider: embeddings)
21
+ MemoryStore.new(embedding_provider: embeddings, cipher: resolved_cipher)
21
22
  end
22
23
  end
23
24
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Llmemory
4
- VERSION = "0.2.1"
4
+ VERSION = "0.2.3"
5
5
  end
data/lib/llmemory.rb CHANGED
@@ -4,6 +4,7 @@ require_relative "llmemory/version"
4
4
  require_relative "llmemory/configuration"
5
5
  require_relative "llmemory/provenance"
6
6
  require_relative "llmemory/tokenizer"
7
+ require_relative "llmemory/instrumentation"
7
8
  require_relative "llmemory/memory_module"
8
9
  require_relative "llmemory/forget_log"
9
10
  require_relative "llmemory/llm"
@@ -15,6 +16,7 @@ require_relative "llmemory/vector_store"
15
16
  require_relative "llmemory/maintenance"
16
17
  require_relative "llmemory/extractors"
17
18
  require_relative "llmemory/reflection"
19
+ require_relative "llmemory/skill_mining"
18
20
  require_relative "llmemory/actions"
19
21
  require_relative "llmemory/memory"
20
22
 
@@ -24,3 +26,5 @@ module Llmemory
24
26
  class StoreError < Error; end
25
27
  class LLMError < Error; end
26
28
  end
29
+
30
+ require_relative "llmemory/crypto/cipher"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: llmemory
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - llmemory
@@ -106,21 +106,33 @@ files:
106
106
  - LICENSE.txt
107
107
  - README.md
108
108
  - app/controllers/llmemory/dashboard/application_controller.rb
109
+ - app/controllers/llmemory/dashboard/episodic_controller.rb
110
+ - app/controllers/llmemory/dashboard/forget_log_controller.rb
109
111
  - app/controllers/llmemory/dashboard/graph_controller.rb
110
112
  - app/controllers/llmemory/dashboard/long_term_controller.rb
113
+ - app/controllers/llmemory/dashboard/maintenance_controller.rb
114
+ - app/controllers/llmemory/dashboard/procedural_controller.rb
115
+ - app/controllers/llmemory/dashboard/reflection_controller.rb
111
116
  - app/controllers/llmemory/dashboard/search_controller.rb
112
117
  - app/controllers/llmemory/dashboard/short_term_controller.rb
113
118
  - app/controllers/llmemory/dashboard/stats_controller.rb
114
119
  - app/controllers/llmemory/dashboard/users_controller.rb
120
+ - app/controllers/llmemory/dashboard/working_controller.rb
115
121
  - app/views/layouts/application.html.erb
122
+ - app/views/llmemory/dashboard/episodic/index.html.erb
123
+ - app/views/llmemory/dashboard/forget_log/show.html.erb
116
124
  - app/views/llmemory/dashboard/graph/index.html.erb
117
125
  - app/views/llmemory/dashboard/long_term/categories.html.erb
118
126
  - app/views/llmemory/dashboard/long_term/index.html.erb
127
+ - app/views/llmemory/dashboard/maintenance/show.html.erb
128
+ - app/views/llmemory/dashboard/procedural/index.html.erb
129
+ - app/views/llmemory/dashboard/reflection/show.html.erb
119
130
  - app/views/llmemory/dashboard/search/index.html.erb
120
131
  - app/views/llmemory/dashboard/short_term/show.html.erb
121
132
  - app/views/llmemory/dashboard/stats/index.html.erb
122
133
  - app/views/llmemory/dashboard/users/index.html.erb
123
134
  - app/views/llmemory/dashboard/users/show.html.erb
135
+ - app/views/llmemory/dashboard/working/show.html.erb
124
136
  - config/routes.rb
125
137
  - exe/llmemory
126
138
  - exe/llmemory-mcp
@@ -140,7 +152,9 @@ files:
140
152
  - lib/llmemory/cli/commands/long_term/graph.rb
141
153
  - lib/llmemory/cli/commands/long_term/nodes.rb
142
154
  - lib/llmemory/cli/commands/long_term/resources.rb
155
+ - lib/llmemory/cli/commands/maintain.rb
143
156
  - lib/llmemory/cli/commands/mcp.rb
157
+ - lib/llmemory/cli/commands/mine_skills.rb
144
158
  - lib/llmemory/cli/commands/procedural.rb
145
159
  - lib/llmemory/cli/commands/search.rb
146
160
  - lib/llmemory/cli/commands/short_term.rb
@@ -148,12 +162,15 @@ files:
148
162
  - lib/llmemory/cli/commands/users.rb
149
163
  - lib/llmemory/cli/commands/working.rb
150
164
  - lib/llmemory/configuration.rb
165
+ - lib/llmemory/crypto/cipher.rb
166
+ - lib/llmemory/crypto/field_helpers.rb
151
167
  - lib/llmemory/dashboard.rb
152
168
  - lib/llmemory/dashboard/engine.rb
153
169
  - lib/llmemory/extractors.rb
154
170
  - lib/llmemory/extractors/entity_relation_extractor.rb
155
171
  - lib/llmemory/extractors/fact_extractor.rb
156
172
  - lib/llmemory/forget_log.rb
173
+ - lib/llmemory/instrumentation.rb
157
174
  - lib/llmemory/llm.rb
158
175
  - lib/llmemory/llm/anthropic.rb
159
176
  - lib/llmemory/llm/base.rb
@@ -204,10 +221,12 @@ files:
204
221
  - lib/llmemory/long_term/procedural/storages/file_storage.rb
205
222
  - lib/llmemory/long_term/procedural/storages/memory_storage.rb
206
223
  - lib/llmemory/maintenance.rb
224
+ - lib/llmemory/maintenance/cognitive_pass.rb
207
225
  - lib/llmemory/maintenance/consolidator.rb
208
226
  - lib/llmemory/maintenance/reindexer.rb
209
227
  - lib/llmemory/maintenance/runner.rb
210
228
  - lib/llmemory/maintenance/summarizer.rb
229
+ - lib/llmemory/maintenance/ttl_expiry.rb
211
230
  - lib/llmemory/mcp.rb
212
231
  - lib/llmemory/mcp/authentication.rb
213
232
  - lib/llmemory/mcp/server.rb
@@ -217,6 +236,8 @@ files:
217
236
  - lib/llmemory/mcp/tools/memory_episodes.rb
218
237
  - lib/llmemory/mcp/tools/memory_forget.rb
219
238
  - lib/llmemory/mcp/tools/memory_info.rb
239
+ - lib/llmemory/mcp/tools/memory_maintain.rb
240
+ - lib/llmemory/mcp/tools/memory_mine_skills.rb
220
241
  - lib/llmemory/mcp/tools/memory_retrieve.rb
221
242
  - lib/llmemory/mcp/tools/memory_save.rb
222
243
  - lib/llmemory/mcp/tools/memory_search.rb
@@ -251,6 +272,8 @@ files:
251
272
  - lib/llmemory/short_term/stores/memory_store.rb
252
273
  - lib/llmemory/short_term/stores/postgres_store.rb
253
274
  - lib/llmemory/short_term/stores/redis_store.rb
275
+ - lib/llmemory/skill_mining.rb
276
+ - lib/llmemory/skill_mining/miner.rb
254
277
  - lib/llmemory/tokenizer.rb
255
278
  - lib/llmemory/vector_store.rb
256
279
  - lib/llmemory/vector_store/active_record_embedding.rb