legionio 1.9.34 → 1.9.36

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: e0fb2242e4fffe902c79cac15713bb57d8b2dd6388f122a9a6f14271faa3b3b2
4
- data.tar.gz: c70d462ff6369c61dd5ae9bd786b325e4e53789eddc78d10d97441793eec1360
3
+ metadata.gz: 1de97753e1f584403f30a43a47049183871b98e7581bee36c892aac9e758983b
4
+ data.tar.gz: 2042c10f023e59093f60258a65ccec746128c3efef7bb980ee33206dafef446c
5
5
  SHA512:
6
- metadata.gz: d7f43d17ba3a681ea09e82678831ba5694a50af90d508eb27acdcc6743d47c74bfec7da597b6d2fe6805e968d8ba247be571f6e26e60d1cfa86d6814974eb9ec
7
- data.tar.gz: 198d3a7b98bb187f23f0e5cfedacabd41983dd52b0ae238650197d2ce9a4888016d211123579cd287e5a2b05f496b0dba6fb627b6280124cf39ca92b11917a18
6
+ metadata.gz: e206bc92436c737aa2046580af9b705d147d8601601703980d69944afbcc10189aa479527ee6f0da128eb0a80912d54dbc635f2c32eab4aaba2f7f21e595062e
7
+ data.tar.gz: da6fc109223bf64dced09fd289f86a3f67592d2ad9a096f64376e0ecea074d2611671eda9fecadbfc14e528a377ee1c2a06e15cc2cbde0de7ffda24c524dab3f
data/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.9.36] - 2026-05-22
6
+
7
+ ### Fixed
8
+ - Identity: preload identity provider gems and resolve process identity before LLM setup so `llm.registry` availability events include Legion identity headers.
9
+ - Identity: use the persisted `identity.json` value as a cached resolver fallback ahead of unverified system identity when fresh auth providers are unavailable.
10
+ - Bundler: load sibling Legion and LLM provider path dependencies outside the test group when those local checkouts exist, so local service boots can use the active workspace gems.
11
+
12
+ ## [1.9.35] - 2026-05-22
13
+
14
+ ### Added
15
+ - CLI: `legionio service start|stop|restart|status` subcommand for direct launchd control
16
+ - Logging transport forwarding now publishes structured log headers/properties, including identity and Legion version headers supplied by `legion-logging`.
17
+
18
+ ### Fixed
19
+ - CLI: `legionio bootstrap --start` now calls `launchctl kickstart` after brew services start to force immediate spawn on macOS 26+ (Tahoe defers `RunAtLoad` for mid-session bootstraps)
20
+
5
21
  ## [1.9.34] - 2026-05-18
6
22
 
7
23
  ### Added
data/Gemfile CHANGED
@@ -8,36 +8,39 @@ gem 'pg'
8
8
  gem 'kramdown', '>= 2.0'
9
9
  gem 'mysql2'
10
10
 
11
- group :test do
12
- gem 'legion-data', path: '../legion-data' if File.exist?(File.expand_path('../legion-data', __dir__))
13
- gem 'legion-logging', path: '../legion-logging' if File.exist?(File.expand_path('../legion-logging', __dir__))
14
- gem 'legion-settings', path: '../legion-settings' if File.exist?(File.expand_path('../legion-settings', __dir__))
15
-
16
- gem 'legion-apollo', path: '../legion-apollo' if File.exist?(File.expand_path('../legion-apollo', __dir__))
17
- gem 'legion-gaia', path: '../legion-gaia' if File.exist?(File.expand_path('../legion-gaia', __dir__))
18
- gem 'legion-llm', path: '../legion-llm' if File.exist?(File.expand_path('../legion-llm', __dir__))
19
- gem 'legion-mcp', path: '../legion-mcp' if File.exist?(File.expand_path('../legion-mcp', __dir__))
20
- gem 'legion-tty', path: '../legion-tty' if File.exist?(File.expand_path('../legion-tty', __dir__))
21
-
22
- gem 'lex-apollo', path: '../extensions/lex-apollo' if File.exist?(File.expand_path('../extensions/lex-apollo', __dir__))
23
- gem 'lex-llm', path: '../extensions-ai/lex-llm' if File.exist?(File.expand_path('../extensions-ai/lex-llm', __dir__))
24
- gem 'lex-llm-ledger', path: '../extensions-ai/lex-llm-ledger' if File.exist?(File.expand_path('../extensions-ai/lex-llm-ledger', __dir__))
25
-
26
- if File.exist?(File.expand_path('../extensions-identity/lex-identity-entra', __dir__))
27
- gem 'lex-identity-entra', path: '../extensions-identity/lex-identity-entra'
28
- end
29
- if File.exist?(File.expand_path('../extensions-identity/lex-identity-kerberos', __dir__))
30
- gem 'lex-identity-kerberos', path: '../extensions-identity/lex-identity-kerberos'
31
- end
32
- if File.exist?(File.expand_path('../extensions-identity/lex-identity-system', __dir__))
33
- gem 'lex-identity-system', path: '../extensions-identity/lex-identity-system'
34
- end
35
-
36
- %w[anthropic azure-foundry bedrock gemini mlx ollama openai vertex vllm].each do |provider|
37
- provider_path = "../extensions-ai/lex-llm-#{provider}"
38
- gem "lex-llm-#{provider}", path: provider_path if File.exist?(File.expand_path(provider_path, __dir__))
39
- end
11
+ gem 'legion-data', path: '../legion-data' if File.exist?(File.expand_path('../legion-data', __dir__))
12
+ gem 'legion-logging', path: '../legion-logging' if File.exist?(File.expand_path('../legion-logging', __dir__))
13
+ gem 'legion-settings', path: '../legion-settings' if File.exist?(File.expand_path('../legion-settings', __dir__))
14
+
15
+ gem 'legion-apollo', path: '../legion-apollo' if File.exist?(File.expand_path('../legion-apollo', __dir__))
16
+ gem 'legion-gaia', path: '../legion-gaia' if File.exist?(File.expand_path('../legion-gaia', __dir__))
17
+ gem 'legion-llm', path: '../legion-llm' if File.exist?(File.expand_path('../legion-llm', __dir__))
18
+ gem 'legion-mcp', path: '../legion-mcp' if File.exist?(File.expand_path('../legion-mcp', __dir__))
19
+ gem 'legion-tty', path: '../legion-tty' if File.exist?(File.expand_path('../legion-tty', __dir__))
20
+
21
+ gem 'lex-apollo', path: '../extensions/lex-apollo' if File.exist?(File.expand_path('../extensions/lex-apollo', __dir__))
22
+ gem 'lex-llm', path: '../extensions-ai/lex-llm' if File.exist?(File.expand_path('../extensions-ai/lex-llm', __dir__))
23
+ # gem 'lex-llm-ledger', path: '../extensions-ai/lex-llm-ledger' if File.exist?(File.expand_path('../extensions-ai/lex-llm-ledger', __dir__))
24
+
25
+ if File.exist?(File.expand_path('../extensions-identity/lex-identity-entra', __dir__))
26
+ gem 'lex-identity-entra', path: '../extensions-identity/lex-identity-entra'
27
+ end
28
+ if File.exist?(File.expand_path('../extensions-identity/lex-identity-kerberos', __dir__))
29
+ gem 'lex-identity-kerberos', path: '../extensions-identity/lex-identity-kerberos'
30
+ end
40
31
 
32
+ gem 'lex-kerberos', path: '../extensions-identity/lex-kerberos' if File.exist?(File.expand_path('../extensions-identity/lex-kerberos', __dir__))
33
+
34
+ if File.exist?(File.expand_path('../extensions-identity/lex-identity-system', __dir__))
35
+ gem 'lex-identity-system', path: '../extensions-identity/lex-identity-system'
36
+ end
37
+
38
+ %w[anthropic azure-foundry bedrock gemini mlx ollama openai vertex vllm].each do |provider|
39
+ provider_path = "../extensions-ai/lex-llm-#{provider}"
40
+ gem "lex-llm-#{provider}", path: provider_path if File.exist?(File.expand_path(provider_path, __dir__))
41
+ end
42
+
43
+ group :test do
41
44
  gem 'faraday'
42
45
  gem 'faraday-net_http'
43
46
  gem 'graphql'
@@ -3,6 +3,7 @@
3
3
  require 'English'
4
4
  require 'json'
5
5
  require 'fileutils'
6
+ require 'open3'
6
7
  require 'rbconfig'
7
8
  require 'thor'
8
9
  require 'legion/cli/output'
@@ -391,18 +392,29 @@ module Legion
391
392
 
392
393
  def run_brew_service(service, out)
393
394
  output, success = shell_capture("brew services start #{service}")
394
- if success
395
- out.success("#{service} started") unless options[:json]
396
- true
397
- else
395
+ unless success
398
396
  out.warn("#{service} failed to start: #{output.strip.lines.last&.strip}") unless options[:json]
399
- false
397
+ return false
400
398
  end
399
+
400
+ out.success("#{service} started") unless options[:json]
401
+ kickstart_launchd_service("homebrew.mxcl.#{service}", out)
401
402
  rescue StandardError => e
402
403
  out.warn("brew services start #{service} raised: #{e.message}") unless options[:json]
403
404
  false
404
405
  end
405
406
 
407
+ def kickstart_launchd_service(label, out)
408
+ return true unless RbConfig::CONFIG['host_os'] =~ /darwin/
409
+
410
+ uid = ::Process.uid
411
+ _, status = Open3.capture2e('launchctl', 'kickstart', "gui/#{uid}/#{label}")
412
+ return true if status.success?
413
+
414
+ out.warn("launchctl kickstart #{label} failed (service may already be running)") unless options[:json]
415
+ false
416
+ end
417
+
406
418
  def poll_daemon_ready(out, port: 4567, timeout: 30)
407
419
  require 'net/http'
408
420
  deadline = ::Time.now + timeout
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open3'
4
+ require 'rbconfig'
5
+ require 'thor'
6
+ require 'legion/cli/output'
7
+
8
+ module Legion
9
+ module CLI
10
+ class ServiceCommand < Thor
11
+ namespace 'service'
12
+
13
+ def self.exit_on_failure?
14
+ true
15
+ end
16
+
17
+ class_option :json, type: :boolean, default: false, desc: 'Output as JSON'
18
+ class_option :no_color, type: :boolean, default: false, desc: 'Disable color output'
19
+
20
+ SERVICE_LABEL = 'homebrew.mxcl.legionio'
21
+
22
+ desc 'start', 'Start the Legion launchd service'
23
+ long_desc <<~DESC
24
+ Starts the Legion background service via launchd. On macOS 26+ (Tahoe),
25
+ uses launchctl kickstart to ensure immediate process spawn after bootstrap.
26
+ DESC
27
+ def start
28
+ out = Output::Formatter.new(json: options[:json], color: !options[:no_color])
29
+ ensure_macos!(out)
30
+
31
+ plist = plist_path
32
+ unless File.exist?(plist)
33
+ out.error("Service plist not found at #{plist}")
34
+ out.info('Run: brew install legionio')
35
+ raise SystemExit, 1
36
+ end
37
+
38
+ uid = ::Process.uid
39
+ target = "gui/#{uid}"
40
+
41
+ if service_loaded?(target)
42
+ out.info('Service already loaded, kicking...')
43
+ else
44
+ _, status = Open3.capture2e('launchctl', 'bootstrap', target, plist)
45
+ out.warn('bootstrap failed (may already be loaded), attempting kickstart anyway') unless status.success?
46
+ end
47
+
48
+ _, status = Open3.capture2e('launchctl', 'kickstart', '-k', "#{target}/#{SERVICE_LABEL}")
49
+ if status.success?
50
+ out.success('Legion service started')
51
+ else
52
+ out.error('Failed to kickstart Legion service')
53
+ raise SystemExit, 1
54
+ end
55
+
56
+ poll_ready(out)
57
+ end
58
+
59
+ desc 'stop', 'Stop the Legion launchd service'
60
+ def stop
61
+ out = Output::Formatter.new(json: options[:json], color: !options[:no_color])
62
+ ensure_macos!(out)
63
+
64
+ uid = ::Process.uid
65
+ target = "gui/#{uid}"
66
+
67
+ _, status = Open3.capture2e('launchctl', 'bootout', "#{target}/#{SERVICE_LABEL}")
68
+ if status.success?
69
+ out.success('Legion service stopped')
70
+ else
71
+ out.warn('Service was not loaded (already stopped?)')
72
+ end
73
+ end
74
+
75
+ desc 'restart', 'Restart the Legion launchd service'
76
+ def restart
77
+ out = Output::Formatter.new(json: options[:json], color: !options[:no_color])
78
+ ensure_macos!(out)
79
+
80
+ uid = ::Process.uid
81
+ target = "gui/#{uid}"
82
+
83
+ Open3.capture2e('launchctl', 'bootout', "#{target}/#{SERVICE_LABEL}")
84
+ sleep 1
85
+
86
+ plist = plist_path
87
+ Open3.capture2e('launchctl', 'bootstrap', target, plist) if File.exist?(plist)
88
+
89
+ _, status = Open3.capture2e('launchctl', 'kickstart', '-k', "#{target}/#{SERVICE_LABEL}")
90
+ if status.success?
91
+ out.success('Legion service restarted')
92
+ else
93
+ out.error('Failed to restart Legion service')
94
+ raise SystemExit, 1
95
+ end
96
+
97
+ poll_ready(out)
98
+ end
99
+
100
+ desc 'status', 'Show Legion launchd service status'
101
+ def status
102
+ out = Output::Formatter.new(json: options[:json], color: !options[:no_color])
103
+ ensure_macos!(out)
104
+
105
+ uid = ::Process.uid
106
+ target = "gui/#{uid}"
107
+ output, status = Open3.capture2e('launchctl', 'print', "#{target}/#{SERVICE_LABEL}")
108
+
109
+ unless status.success?
110
+ out.info('Service is not loaded')
111
+ return
112
+ end
113
+
114
+ state = output[/state = (.+)/, 1] || 'unknown'
115
+ pid = output[/pid = (\d+)/, 1]
116
+ runs = output[/runs = (\d+)/, 1]
117
+
118
+ if options[:json]
119
+ puts Legion::JSON.dump({ state: state, pid: pid&.to_i, runs: runs&.to_i })
120
+ else
121
+ out.info("State: #{state}")
122
+ out.info("PID: #{pid}") if pid
123
+ out.info("Runs: #{runs}") if runs
124
+ end
125
+ end
126
+
127
+ private
128
+
129
+ def ensure_macos!(out)
130
+ return if RbConfig::CONFIG['host_os'] =~ /darwin/
131
+
132
+ out.error('The service command is only available on macOS (uses launchd)')
133
+ raise SystemExit, 1
134
+ end
135
+
136
+ def plist_path
137
+ File.expand_path("~/Library/LaunchAgents/#{SERVICE_LABEL}.plist")
138
+ end
139
+
140
+ def service_loaded?(target)
141
+ _, status = Open3.capture2e('launchctl', 'print', "#{target}/#{SERVICE_LABEL}")
142
+ status.success?
143
+ end
144
+
145
+ def poll_ready(out, port: 4567, timeout: 15)
146
+ require 'net/http'
147
+ deadline = ::Time.now + timeout
148
+ until ::Time.now > deadline
149
+ begin
150
+ resp = Net::HTTP.get_response(URI("http://localhost:#{port}/api/ready"))
151
+ if resp.is_a?(Net::HTTPSuccess)
152
+ out.success("Daemon ready on port #{port}")
153
+ return
154
+ end
155
+ rescue StandardError
156
+ # not ready yet
157
+ end
158
+ sleep 1
159
+ end
160
+ out.info('Service started but not yet ready (boot in progress)')
161
+ end
162
+ end
163
+ end
164
+ end
data/lib/legion/cli.rb CHANGED
@@ -69,6 +69,7 @@ module Legion
69
69
  autoload :Debug, 'legion/cli/debug_command'
70
70
  autoload :CodegenCommand, 'legion/cli/codegen_command'
71
71
  autoload :Bootstrap, 'legion/cli/bootstrap_command'
72
+ autoload :ServiceCommand, 'legion/cli/service_command'
72
73
  autoload :Broker, 'legion/cli/broker_command'
73
74
  autoload :AdminCommand, 'legion/cli/admin_command'
74
75
  autoload :Workflow, 'legion/cli/workflow_command'
@@ -258,6 +259,9 @@ module Legion
258
259
  desc 'setup SUBCOMMAND', 'Install feature packs and configure IDE integrations'
259
260
  subcommand 'setup', Legion::CLI::Setup
260
261
 
262
+ desc 'service SUBCOMMAND', 'Manage the Legion launchd background service'
263
+ subcommand 'service', Legion::CLI::ServiceCommand
264
+
261
265
  desc 'bootstrap SOURCE', 'One-command setup: fetch config, scaffold, and install packs'
262
266
  subcommand 'bootstrap', Legion::CLI::Bootstrap
263
267
 
@@ -132,6 +132,24 @@ module Legion
132
132
  Legion::Logging.info "[Extensions] flushed #{count} pending registrations" if defined?(Legion::Logging)
133
133
  end
134
134
 
135
+ def require_identity_extensions
136
+ find_extensions.select { |entry| entry[:category] == :identity }.each do |entry|
137
+ gem_name = entry[:gem_name]
138
+ ext_settings = extension_settings_for_entry(entry)
139
+
140
+ if ext_settings.is_a?(Hash) && ext_settings.key?(:enabled) && !ext_settings[:enabled]
141
+ Legion::Logging.info "Skipping #{gem_name} identity preload because it's disabled"
142
+ next
143
+ end
144
+
145
+ Catalog.register(gem_name)
146
+ register_extension_handle(gem_name, state: :registered,
147
+ latest_installed_version: latest_installed_version(gem_name))
148
+ ensure_namespace(entry[:const_path]) if entry[:segments].length > 1
149
+ gem_load(entry)
150
+ end
151
+ end
152
+
135
153
  def pause_actors
136
154
  @running_instances&.each do |inst|
137
155
  timer = inst.instance_variable_get(:@timer)
@@ -21,6 +21,10 @@ module Legion
21
21
  token
22
22
  end
23
23
 
24
+ def credential_for(provider_name, qualifier: nil, for_context: nil, purpose: nil, context: nil)
25
+ token_for(provider_name, qualifier: qualifier, for_context: for_context, purpose: purpose, context: context)
26
+ end
27
+
24
28
  def lease_for(provider_name, qualifier: nil)
25
29
  name = provider_name.to_sym
26
30
  resolved = qualifier || default_qualifier_for(name)
@@ -33,6 +33,12 @@ module Legion
33
33
 
34
34
  winning_provider, winning_result, provider_results = resolve_auth(auth_providers, timeout: timeout)
35
35
 
36
+ if winning_provider.nil?
37
+ log.debug('resolve!: no auth winner, trying cached identity')
38
+ winning_provider, winning_result, cached_results = resolve_cached_identity
39
+ provider_results.merge!(cached_results) if cached_results
40
+ end
41
+
36
42
  if winning_provider.nil?
37
43
  log.debug('resolve!: no auth winner, trying fallback providers')
38
44
  winning_provider, winning_result, fallback_results = resolve_auth(fallback_providers, timeout: timeout)
@@ -229,6 +235,67 @@ module Legion
229
235
  end
230
236
  end
231
237
 
238
+ def resolve_cached_identity
239
+ cached = read_cached_identity
240
+ return [nil, nil, {}] unless cached
241
+
242
+ provider = cached_identity_provider
243
+ result = {
244
+ canonical_name: cached[:canonical_name],
245
+ kind: cached[:kind] || :human,
246
+ source: :identity_json,
247
+ persistent: true
248
+ }
249
+
250
+ [
251
+ provider,
252
+ result,
253
+ {
254
+ provider.provider_name => {
255
+ status: :resolved,
256
+ trust: provider.trust_level,
257
+ resolved_at: Time.now,
258
+ provider: provider,
259
+ result: result
260
+ }
261
+ }
262
+ ]
263
+ end
264
+
265
+ def read_cached_identity
266
+ path = File.expand_path('~/.legionio/settings/identity.json')
267
+ return nil unless File.file?(path)
268
+
269
+ data = if defined?(Legion::JSON)
270
+ Legion::JSON.load(File.read(path))
271
+ else
272
+ require 'json'
273
+ ::JSON.parse(File.read(path), symbolize_names: true)
274
+ end
275
+ canonical = data[:canonical_name] || data['canonical_name']
276
+ return nil if canonical.to_s.strip.empty?
277
+
278
+ {
279
+ canonical_name: canonical.to_s,
280
+ kind: (data[:kind] || data['kind'] || :human).to_sym
281
+ }
282
+ rescue StandardError => e
283
+ log.warn("identity.json read failed: #{e.message}")
284
+ nil
285
+ end
286
+
287
+ def cached_identity_provider
288
+ @cached_identity_provider ||= Module.new do
289
+ module_function
290
+
291
+ def provider_name = :identity_cache
292
+ def provider_type = :auth
293
+ def priority = -100
294
+ def trust_weight = 150
295
+ def trust_level = :cached
296
+ end
297
+ end
298
+
232
299
  def auth_future_status(future, result)
233
300
  if future.rejected?
234
301
  :failed
@@ -112,6 +112,8 @@ module Legion
112
112
  end
113
113
  setup_cluster if data
114
114
 
115
+ setup_identity_before_llm(extensions: extensions, transport: transport)
116
+
115
117
  if llm
116
118
  begin
117
119
  setup_llm
@@ -161,15 +163,14 @@ module Legion
161
163
  setup_safety_metrics
162
164
  setup_supervision if supervision
163
165
 
164
- require_relative 'identity' if File.exist?(File.expand_path('identity.rb', __dir__))
165
-
166
166
  if extensions
167
167
  load_extensions
168
168
  Legion::Readiness.mark_ready(:extensions)
169
169
  setup_generated_functions
170
170
  end
171
171
 
172
- # Identity resolution after extensions so lex-identity-* providers are loaded
172
+ # Re-run identity after full extension load so any providers with autobuild-time
173
+ # registration can upgrade the pre-LLM identity.
173
174
  db_available = defined?(Legion::Data) && Legion::Data.respond_to?(:connected?) && Legion::Data.connected?
174
175
  setup_identity if transport || db_available
175
176
  register_credential_providers if extensions && (transport || db_available)
@@ -589,7 +590,7 @@ module Legion
589
590
  end
590
591
  end
591
592
 
592
- def setup_logging_transport # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
593
+ def setup_logging_transport # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
593
594
  return unless defined?(Legion::Transport::Connection)
594
595
  return unless Legion::Transport::Connection.session_open?
595
596
 
@@ -612,11 +613,16 @@ module Legion
612
613
  exchange = log_channel.topic('legion.logging', durable: true)
613
614
 
614
615
  if forward_logs
615
- Legion::Logging.log_writer = lambda { |event, routing_key:|
616
+ Legion::Logging.log_writer = lambda { |event, routing_key:, headers: {}, properties: {}|
616
617
  begin
617
618
  next unless log_channel&.open?
618
619
 
619
- exchange.publish(Legion::JSON.dump(event), routing_key: routing_key)
620
+ exchange.publish(
621
+ Legion::JSON.dump(event),
622
+ routing_key: routing_key,
623
+ headers: headers,
624
+ **properties
625
+ )
620
626
  rescue StandardError
621
627
  nil
622
628
  end
@@ -918,6 +924,8 @@ module Legion
918
924
  Legion::Readiness.mark_skipped(:rbac)
919
925
  end
920
926
 
927
+ setup_identity_before_llm(extensions: true, transport: true)
928
+
921
929
  if defined?(Legion::LLM)
922
930
  setup_llm
923
931
  else
@@ -945,7 +953,6 @@ module Legion
945
953
  # Phase 5: re-run identity resolution after extensions are loaded so that
946
954
  # any identity providers registered by lex-identity-* extensions are
947
955
  # available to the resolver (mirrors the boot-time ordering).
948
- Legion::Identity::Resolver.reset! if defined?(Legion::Identity::Resolver)
949
956
  setup_identity
950
957
 
951
958
  db_available = defined?(Legion::Data) && Legion::Data.respond_to?(:connected?) && Legion::Data.connected?
@@ -976,6 +983,18 @@ module Legion
976
983
  Legion::Extensions.hook_extensions
977
984
  end
978
985
 
986
+ def setup_identity_before_llm(extensions:, transport:)
987
+ require_relative 'identity' if File.exist?(File.expand_path('identity.rb', __dir__))
988
+ Legion::Extensions.require_identity_extensions if extensions &&
989
+ defined?(Legion::Extensions) &&
990
+ Legion::Extensions.respond_to?(:require_identity_extensions)
991
+
992
+ db_available = defined?(Legion::Data) && Legion::Data.respond_to?(:connected?) && Legion::Data.connected?
993
+ setup_identity if transport || db_available
994
+ rescue StandardError => e
995
+ handle_exception(e, level: :warn, operation: 'service.setup_identity_before_llm')
996
+ end
997
+
979
998
  def register_core_tools
980
999
  require 'legion/tools'
981
1000
  Legion::Tools.register_all
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Legion
4
- VERSION = '1.9.34'
4
+ VERSION = '1.9.36'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legionio
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.34
4
+ version: 1.9.36
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -750,6 +750,7 @@ files:
750
750
  - lib/legion/cli/relationship.rb
751
751
  - lib/legion/cli/review_command.rb
752
752
  - lib/legion/cli/schedule_command.rb
753
+ - lib/legion/cli/service_command.rb
753
754
  - lib/legion/cli/setup_command.rb
754
755
  - lib/legion/cli/skill_command.rb
755
756
  - lib/legion/cli/start.rb