legion-tty 0.4.38 → 0.4.39

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: 5015163992ac8b889c19088f2caa60b3e80fc09445c7feb27085f4c39c0857a5
4
- data.tar.gz: 6eadf44a9366956867b097b1a10769ae18b01d1a644daae91c3ea5fade6681b3
3
+ metadata.gz: 7a46889a85e5da8000964e9ac91d0c8e8fba67591fc12ca4bc8a4057339ed09b
4
+ data.tar.gz: d769157e7b76054bc01148650474b0e621a22173428628ce2edf41e2c58aafc8
5
5
  SHA512:
6
- metadata.gz: 914de3d2b07691224655ac5b1dd4bb7b1dd9b88083fde6a0b719c9398a654c0b91640c621ea88865d11ad8b737909af0a24dcbae9ff1e4d200be8eda58d14af6
7
- data.tar.gz: dd740544c87c71fa843e72ad602f49d029cd485f16d4b2532e55927ce33b2019042772812f59281aa00633c75af9ed5b3ce0f89c84070bd250ce51c27db304bf
6
+ metadata.gz: cfb3a079a43835daf8cb922627ccb2f19e36c9fcfd0577f9cfa9a468f38d0ff1f658eaaeeda82ff202b6213013442e798cca11e9b61ca544ce171173ada7ce8e
7
+ data.tar.gz: 4bc9d5d5e2b7173740d4d1a72193e31d77c7bd22bfa745cd39861704804d2f78489dafc915c5a35b916252a554b80df685b4e0bb6136f0cc1bef356b24e57a0f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.39] - 2026-03-28
4
+
5
+ ### Fixed
6
+ - Onboarding wizard now installs GAIA cognitive extension gems when waking GAIA: after the daemon starts (or is already running), `offer_gaia_gems` checks for the 21 agentic/GAIA-tier gems via `Gem::Specification.find_by_name` and prompts the user to install any that are missing
7
+ - `LlmProbe` now returns `:configured` status (instead of `:error`) when a provider is enabled but the ping fails (e.g. Vault-injected providers unknown to `apply_provider_config`); these providers are shown with a key icon and "configured, not validated" label and are selectable as the default when no fully-validated `:ok` providers exist
8
+ - `LlmProbe` accepts a `wait_queue:` argument so it waits for the bootstrap config thread to complete before probing, ensuring Vault-sourced provider settings are written to `~/.legionio/settings/llm.json` first
9
+ - `select_provider_default` falls back to `:configured` providers when no `:ok` providers exist, so Vault-resolved providers are usable as the chat default
10
+
3
11
  ## [0.4.38] - 2026-03-26
4
12
 
5
13
  ### Added
@@ -4,12 +4,14 @@ module Legion
4
4
  module TTY
5
5
  module Background
6
6
  class LlmProbe
7
- def initialize(logger: nil)
7
+ def initialize(logger: nil, wait_queue: nil)
8
8
  @log = logger
9
+ @wait_queue = wait_queue
9
10
  end
10
11
 
11
12
  def run_async(queue)
12
13
  Thread.new do
14
+ wait_for_bootstrap if @wait_queue
13
15
  result = probe_providers
14
16
  queue.push({ data: result })
15
17
  rescue StandardError => e
@@ -20,6 +22,28 @@ module Legion
20
22
 
21
23
  private
22
24
 
25
+ def wait_for_bootstrap
26
+ deadline = Time.now + 15
27
+ timed_out = false
28
+ loop do
29
+ break unless @wait_queue.empty?
30
+
31
+ if Time.now >= deadline
32
+ timed_out = true
33
+ break
34
+ end
35
+
36
+ sleep 0.2
37
+ end
38
+ if timed_out
39
+ @log&.log('llm_probe', 'bootstrap wait timed out')
40
+ else
41
+ @log&.log('llm_probe', 'bootstrap wait complete')
42
+ end
43
+ rescue StandardError => e
44
+ @log&.log('llm_probe', "bootstrap wait error: #{e.message}")
45
+ end
46
+
23
47
  def probe_providers
24
48
  require 'legion/llm'
25
49
  require 'legion/settings'
@@ -54,7 +78,7 @@ module Legion
54
78
  rescue StandardError => e
55
79
  latency = ((Time.now - start_time) * 1000).round
56
80
  Legion::Logging.debug("ping_provider #{name} failed: #{e.message}") if defined?(Legion::Logging)
57
- { name: name, model: model, status: :error, latency_ms: latency, error: e.message }
81
+ { name: name, model: model, status: :configured, latency_ms: latency, error: e.message }
58
82
  end
59
83
  end
60
84
  end
@@ -56,10 +56,14 @@ module Legion
56
56
 
57
57
  def display_provider_results(providers)
58
58
  providers.each do |p|
59
- icon = p[:status] == :ok ? "\u2705" : "\u274C"
59
+ icon = case p[:status]
60
+ when :ok then "\u2705"
61
+ when :configured then "\U0001F511"
62
+ else "\u274C"
63
+ end
60
64
  latency = "#{p[:latency_ms]}ms"
61
65
  label = "#{icon} #{p[:name]} (#{p[:model]}) \u2014 #{latency}"
62
- label += " [#{p[:error]}]" if p[:error]
66
+ label += p[:status] == :configured ? ' [configured, not validated]' : " [#{p[:error]}]" if p[:error]
63
67
  @prompt.say(label)
64
68
  end
65
69
  end
@@ -17,6 +17,17 @@ module Legion
17
17
  class Onboarding < Base
18
18
  TYPED_DELAY = 0.05
19
19
 
20
+ GAIA_GEMS = %w[
21
+ lex-agentic-self lex-agentic-affect lex-agentic-attention
22
+ lex-agentic-defense lex-agentic-executive lex-agentic-homeostasis
23
+ lex-agentic-imagination lex-agentic-inference lex-agentic-integration
24
+ lex-agentic-language lex-agentic-learning lex-agentic-memory
25
+ lex-agentic-social
26
+ lex-tick lex-extinction lex-mind-growth lex-mesh
27
+ lex-synapse lex-react
28
+ legion-gaia legion-apollo
29
+ ].freeze
30
+
20
31
  def initialize(app, wizard: nil, output: $stdout, skip_rain: false)
21
32
  super(app)
22
33
  @wizard = wizard || Components::WizardPrompt.new
@@ -119,6 +130,7 @@ module Legion
119
130
 
120
131
  def select_provider_default(providers)
121
132
  working = providers.select { |p| p[:status] == :ok }
133
+ working = providers.select { |p| p[:status] == :configured } if working.empty?
122
134
  if working.any?
123
135
  default = @wizard.select_default_provider(working)
124
136
  sleep 0.5
@@ -139,7 +151,7 @@ module Legion
139
151
  @kerberos_probe.run_async(@kerberos_queue)
140
152
  @github_probe.run_quick_async(@github_quick_queue)
141
153
  require_relative '../background/llm_probe'
142
- @llm_probe = Background::LlmProbe.new(logger: @log)
154
+ @llm_probe = Background::LlmProbe.new(logger: @log, wait_queue: @bootstrap_queue)
143
155
  @llm_probe.run_async(@llm_queue)
144
156
  @bootstrap_probe = Background::BootstrapConfig.new(logger: @log)
145
157
  @bootstrap_probe.run_async(@bootstrap_queue)
@@ -196,37 +208,42 @@ module Legion
196
208
  end
197
209
  end
198
210
 
199
- # rubocop:disable Metrics/AbcSize
200
211
  def run_gaia_awakening
201
212
  typed_output('Scanning for active cognition threads...')
202
213
  sleep 1.2
203
214
  @output.puts
204
215
 
205
- if legionio_running?
206
- typed_output('GAIA is awake.')
207
- sleep 0.5
208
- typed_output('Heuristic mesh: nominal.')
209
- sleep 0.8
210
- typed_output('Cognitive threads synchronized.')
211
- else
212
- typed_output('GAIA is dormant.')
213
- sleep 1
214
- @output.puts
215
- if @wizard.confirm('Shall I wake her?')
216
- started = start_legionio_daemon
217
- if started
218
- typed_output('... initializing cognitive substrate...')
219
- sleep 1
220
- typed_output('GAIA online. All systems nominal.')
221
- else
222
- typed_output("Could not start daemon. Run 'legionio start' manually.")
223
- end
224
- end
225
- end
216
+ gaia_active = if legionio_running?
217
+ typed_output('GAIA is awake.')
218
+ sleep 0.5
219
+ typed_output('Heuristic mesh: nominal.')
220
+ sleep 0.8
221
+ typed_output('Cognitive threads synchronized.')
222
+ true
223
+ else
224
+ wake_gaia_daemon
225
+ end
226
226
 
227
+ offer_gaia_gems if gaia_active
227
228
  @output.puts
228
229
  end
229
- # rubocop:enable Metrics/AbcSize
230
+
231
+ def wake_gaia_daemon
232
+ typed_output('GAIA is dormant.')
233
+ sleep 1
234
+ @output.puts
235
+ return unless @wizard.confirm('Shall I wake her?')
236
+
237
+ started = start_legionio_daemon
238
+ if started
239
+ typed_output('... initializing cognitive substrate...')
240
+ sleep 1
241
+ typed_output('GAIA online. All systems nominal.')
242
+ else
243
+ typed_output("Could not start daemon. Run 'legionio start' manually.")
244
+ end
245
+ started
246
+ end
230
247
 
231
248
  def collect_background_results
232
249
  @log.log('collect', 'waiting for scanner results (10s timeout)')
@@ -392,7 +409,7 @@ module Legion
392
409
 
393
410
  def store_teams_token(result)
394
411
  require 'legion/extensions/microsoft_teams/helpers/token_cache'
395
- cache = Legion::Extensions::MicrosoftTeams::Helpers::TokenCache.new
412
+ cache = Legion::Extensions::MicrosoftTeams::Helpers::TokenCache.instance
396
413
  cache.store_delegated_token(result)
397
414
  cache.save_to_vault
398
415
  rescue StandardError => e
@@ -400,6 +417,47 @@ module Legion
400
417
  nil
401
418
  end
402
419
 
420
+ def offer_gaia_gems
421
+ missing = missing_gaia_gems
422
+ return if missing.empty?
423
+
424
+ @output.puts
425
+ typed_output("#{missing.size} cognitive extension#{'s' if missing.size != 1} not installed.")
426
+ @output.puts
427
+ return unless @wizard.confirm('Install cognitive extensions?')
428
+
429
+ install_gaia_gems(missing)
430
+ end
431
+
432
+ def missing_gaia_gems
433
+ GAIA_GEMS.reject do |gem_name|
434
+ Gem::Specification.find_by_name(gem_name)
435
+ true
436
+ rescue Gem::LoadError
437
+ false
438
+ end
439
+ end
440
+
441
+ def install_gaia_gems(gems)
442
+ failed = []
443
+ gems.each do |gem_name|
444
+ typed_output(" installing #{gem_name}...")
445
+ @output.puts
446
+ Gem.install(gem_name)
447
+ rescue StandardError => e
448
+ @log.log('gaia_gems', "failed to install #{gem_name}: #{e.message}")
449
+ typed_output(" failed: #{gem_name}")
450
+ @output.puts
451
+ failed << gem_name
452
+ end
453
+ if failed.empty?
454
+ typed_output('Cognitive extensions installed.')
455
+ else
456
+ typed_output("Cognitive extensions installed with #{failed.size} failure#{'s' if failed.size != 1}.")
457
+ end
458
+ @output.puts
459
+ end
460
+
403
461
  def detect_gem_available?
404
462
  require 'legion/extensions/detect'
405
463
  true
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module TTY
5
- VERSION = '0.4.38'
5
+ VERSION = '0.4.39'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legion-tty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.38
4
+ version: 0.4.39
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity