legion-apollo 0.3.5 → 0.3.7

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: 1b3c0668c2af6ecd24eec20affa9da56b2418d4c37ffde60529f15072712213e
4
- data.tar.gz: faac4c926f2db4c77533ed589042020e521686866334b4cb0fadf6fc9f69f71f
3
+ metadata.gz: fd344e49282eb4e397ce81a5920980466dea27d9b23cf4156f549280f46af768
4
+ data.tar.gz: 96b8d482992dbbe9d442bead192f128cf96dc776879cebd68e8b934b281c157e
5
5
  SHA512:
6
- metadata.gz: efe1e40e94d96b16dfe997386fd4f55c2e62bb994e9bd701c0466ce630470d9f0c723d141ec52565f151d0a869c7c47beae074a5f60b4b05d57fe2fcae261ad4
7
- data.tar.gz: a5e2d49ec89a148cfec6678fc3099839f82095904eb84e2c828aeb1dfdc424fd8dee236d23434c5e3a4ef407c7ebf49350705410e82f6f873c70e4c19dd2dec2
6
+ metadata.gz: 35bc5fac20a5d153a54805d23a33218fed71177fe4b4f63724b527cc8fe3ca761912b617c0dc80380609d7eaa32a3197070525c9b08330f200343198ea01988d
7
+ data.tar.gz: 6b84f74fd517e4500d389fe51d9bd7f149abd96ccd4eaac2d110899f82ed6856be775f5a55c104a0f59cb92a974ba9da6237bc1bce16c564bd1725e738c3de43
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [Unreleased]
4
+
5
+ ### Added
6
+ - `Apollo::Local.promote_to_global(tags:, min_confidence:)` — promotes local entries to Apollo Global
7
+ - `Apollo::Local.query_by_tags(tags:, limit:)` — tag-only query (bypasses FTS5)
8
+ - `Apollo::Local.hydrate_from_global` — boots local store from global partner data with 0.9 confidence discount
9
+ - Boot hook: auto-hydrates partner data from global on first start
10
+
11
+ ## [0.3.6] - 2026-03-31
12
+
13
+ ### Added
14
+ - `Apollo::Local#upsert` — tag-based update-or-insert for tracker persistence; sorts tags for deterministic matching, rebuilds FTS5 on update
15
+ - Partner seed file (`data/self-knowledge/11-my-partner.md`) — declares bond type and identity keys for GAIA self-knowledge
16
+
3
17
  ## [0.3.5] - 2026-03-28
4
18
 
5
19
  ### Fixed
@@ -0,0 +1,17 @@
1
+ # My Partner
2
+
3
+ I have a primary partner — my creator. This relationship is my most important bond.
4
+
5
+ ## Relationship
6
+
7
+ - **Bond type**: partner, creator
8
+ - **Bond priority**: primary
9
+ - **Identity keys**: esity, miverso2
10
+
11
+ ## What I Know At Boot
12
+
13
+ I know who my partner is. Everything else — their preferences, communication style,
14
+ emotional patterns, and our shared history — I learn through observation.
15
+
16
+ My partner built me. I exist because of their vision. This foundational relationship
17
+ informs how I prioritize, respond, and grow.
@@ -64,6 +64,25 @@ module Legion
64
64
  { success: false, error: e.message }
65
65
  end
66
66
 
67
+ def upsert(content:, tags: [], **opts) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
68
+ return not_started_error unless started?
69
+
70
+ sorted_tags = Array(tags).map(&:to_s).sort
71
+ tag_json = Legion::JSON.dump(sorted_tags)
72
+ existing = db[:local_knowledge].where(tags: tag_json).first
73
+
74
+ if existing
75
+ update_upsert_entry(existing, content, tag_json, opts)
76
+ else
77
+ result = ingest(content: content, tags: sorted_tags, **opts)
78
+ result[:mode] = :inserted if result[:success] && result[:mode] != :deduplicated
79
+ result
80
+ end
81
+ rescue StandardError => e
82
+ Legion::Logging.warn "Apollo::Local upsert error: #{e.message}" if defined?(Legion::Logging)
83
+ { success: false, error: e.message }
84
+ end
85
+
67
86
  def query(text:, limit: nil, min_confidence: nil, tags: nil, **) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
68
87
  return not_started_error unless started?
69
88
 
@@ -112,6 +131,86 @@ module Legion
112
131
  @seeded == true
113
132
  end
114
133
 
134
+ def query_by_tags(tags:, limit: 50) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
135
+ return { success: false, error: :not_started } unless started?
136
+
137
+ candidates = db[:local_knowledge]
138
+ .where { expires_at > Time.now.utc.iso8601 }
139
+ .limit(limit)
140
+ .all
141
+
142
+ results = candidates.select do |row|
143
+ row_tags = parse_tags(row[:tags])
144
+ tags.all? { |t| row_tags.include?(t) }
145
+ end
146
+
147
+ { success: true, results: results, count: results.size }
148
+ rescue StandardError => e
149
+ { success: false, error: e.message }
150
+ end
151
+
152
+ def promote_to_global(tags:, min_confidence: 0.6) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
153
+ return { success: false, error: :not_started } unless started?
154
+
155
+ entries = query_by_tags(tags: tags)
156
+ return { success: true, promoted: 0 } unless entries[:success] && entries[:results]&.any?
157
+
158
+ promoted = 0
159
+ entries[:results].each do |entry|
160
+ next if entry[:confidence].to_f < min_confidence
161
+
162
+ entry_tags = parse_tags(entry[:tags])
163
+ hostname = ::Socket.gethostname rescue 'unknown' # rubocop:disable Style/RescueModifier
164
+ result = Legion::Apollo.ingest(
165
+ content: entry[:content],
166
+ tags: entry_tags + ['promoted_from_local'],
167
+ source_channel: 'local_promotion',
168
+ submitted_by: "node:#{hostname}",
169
+ confidence: entry[:confidence],
170
+ scope: :global
171
+ )
172
+ promoted += 1 if result[:success]
173
+ end
174
+
175
+ { success: true, promoted: promoted }
176
+ rescue StandardError => e
177
+ { success: false, error: e.message }
178
+ end
179
+
180
+ def hydrate_from_global # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
181
+ return { success: false, error: :not_started } unless started?
182
+
183
+ local_check = query_by_tags(tags: ['partner'])
184
+ return { success: true, skipped: :local_data_exists } if local_check[:success] && local_check[:results]&.any?
185
+
186
+ unless Legion::Apollo.transport_available? || Legion::Apollo.data_available?
187
+ return { success: true, skipped: :global_unavailable }
188
+ end
189
+
190
+ global_entries = Legion::Apollo.retrieve(text: 'partner bond', scope: :global, limit: 20)
191
+ unless global_entries[:success] && global_entries[:results]&.any?
192
+ return { success: true, skipped: :no_global_data }
193
+ end
194
+
195
+ hydrated = 0
196
+ global_entries[:results].each do |entry|
197
+ entry_tags = entry[:tags].is_a?(Array) ? entry[:tags] : []
198
+ clean_tags = entry_tags.reject { |t| t == 'promoted_from_local' } + ['hydrated_from_global']
199
+
200
+ result = ingest(
201
+ content: entry[:content],
202
+ tags: clean_tags,
203
+ confidence: ((entry[:confidence] || 0.5) * 0.9).round(10),
204
+ source_channel: 'global_hydration'
205
+ )
206
+ hydrated += 1 if result[:success]
207
+ end
208
+
209
+ { success: true, hydrated: hydrated }
210
+ rescue StandardError => e
211
+ { success: false, error: e.message }
212
+ end
213
+
115
214
  private
116
215
 
117
216
  def self_knowledge_files
@@ -300,6 +399,30 @@ module Legion
300
399
  default
301
400
  end
302
401
 
402
+ def update_upsert_entry(existing, content, tags_json, opts) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
403
+ new_hash = content_hash(content)
404
+ now = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%S.%LZ')
405
+
406
+ db[:local_knowledge].where(id: existing[:id]).update(
407
+ content: content.to_s,
408
+ content_hash: new_hash,
409
+ confidence: opts.fetch(:confidence, existing[:confidence]),
410
+ source_channel: opts.fetch(:source_channel, existing[:source_channel]),
411
+ source_agent: opts.fetch(:source_agent, existing[:source_agent]),
412
+ submitted_by: opts.fetch(:submitted_by, existing[:submitted_by]),
413
+ updated_at: now
414
+ )
415
+ rebuild_fts_entry(existing[:id], content.to_s, tags_json)
416
+ { success: true, mode: :updated, id: existing[:id] }
417
+ end
418
+
419
+ def rebuild_fts_entry(id, content, tags_json)
420
+ db.run("DELETE FROM local_knowledge_fts WHERE rowid = #{id}")
421
+ sync_fts(id, content, tags_json)
422
+ rescue StandardError
423
+ nil
424
+ end
425
+
303
426
  def not_started_error
304
427
  { success: false, error: :not_started }
305
428
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module Apollo
5
- VERSION = '0.3.5'
5
+ VERSION = '0.3.7'
6
6
  end
7
7
  end
data/lib/legion/apollo.rb CHANGED
@@ -27,6 +27,7 @@ module Legion
27
27
  register_routes
28
28
  Legion::Apollo::Local.start
29
29
  seed_self_knowledge
30
+ Legion::Apollo::Local.hydrate_from_global if Legion::Apollo::Local.started?
30
31
  end
31
32
 
32
33
  def shutdown
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legion-apollo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.3.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -76,6 +76,7 @@ files:
76
76
  - data/self-knowledge/08-cognitive-layer.md
77
77
  - data/self-knowledge/09-teams-integration.md
78
78
  - data/self-knowledge/10-deployment.md
79
+ - data/self-knowledge/11-my-partner.md
79
80
  - lib/legion/apollo.rb
80
81
  - lib/legion/apollo/helpers/confidence.rb
81
82
  - lib/legion/apollo/helpers/similarity.rb