legionio 1.4.60 → 1.4.61

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: d0b17b55ad4c040c0b2f9e4d23d811b2611791b930dfbf434c5ed99136bae3f6
4
- data.tar.gz: 7ca5c1823f303b3f54096c5c342c067923bb17ed90e6c891bdff55825209d1b4
3
+ metadata.gz: 6c7e2aaea33b6be8c15f2059d61621a70e9e431957e4e574b8ff574d33d40045
4
+ data.tar.gz: 07ea8f95f7b2c5132f8fe7992ccb247d5cec1fe909f3ba927f42bf172fff22ec
5
5
  SHA512:
6
- metadata.gz: c77d435c1fdb2ce380d06d7c36e7099e96f002d3036579e35a49c8c4ce4de419f160dbef83244f529b86cdba7591afe5eea43464943f596c37fdf3a0ee87a16f
7
- data.tar.gz: 7f31653651939165a61d378d4aa0ecf7cc1db427e922288bb7604bafaf4493828c55b4548618b252fcf0000bb54d84b63922759ebf8dd41facfa0eba067c4e3f
6
+ metadata.gz: a5648ed88592cb9eaaac03a285f3862812ff9dbd93ea3a0f8a2bd1ff400e7b2084c3d59e5b2b5fbbf0016ff05f4897291f25f59b62c543ef86b9806aeba6a377
7
+ data.tar.gz: 73b80071c84f14627df14ceca94a9f7201143ca47149244838d649387d82430cfffaf3a1998fcb92f91d6aecc73abc9a8ce151eb6ea626c8237b738554ca9965
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Legion Changelog
2
2
 
3
+ ## [1.4.61] - 2026-03-18
4
+
5
+ ### Added
6
+ - Chat persistent settings defaults via `Legion::Settings` (issue #5)
7
+ - `chat_setting(*keys)` helper for centralized settings access with error handling
8
+ - Settings priority chain: CLI flag > `Legion::Settings.dig(:chat, ...)` > hardcoded default
9
+ - Configurable via settings: model, provider, personality, permissions, markdown, incognito, max_budget_usd, subagent concurrency/timeout, headless max_turns
10
+ - `chat` subsystem added to `config scaffold` with full template
11
+ - `Subagent.configure_from_settings` reads concurrency and timeout from settings
12
+ - 22 new specs (19 settings integration + 3 subagent settings)
13
+
3
14
  ## [1.4.60] - 2026-03-18
4
15
 
5
16
  ### Fixed
data/CLAUDE.md CHANGED
@@ -9,7 +9,7 @@ The primary gem for the LegionIO framework. An extensible async job engine for s
9
9
 
10
10
  **GitHub**: https://github.com/LegionIO/LegionIO
11
11
  **Gem**: `legionio`
12
- **Version**: 1.4.60
12
+ **Version**: 1.4.61
13
13
  **License**: Apache-2.0
14
14
  **Docker**: `legionio/legion`
15
15
  **Ruby**: >= 3.4
@@ -711,7 +711,7 @@ rack-test, rake, rspec, rubocop, rubocop-rspec, simplecov
711
711
 
712
712
  ```bash
713
713
  bundle install
714
- bundle exec rspec # 1357 examples, 0 failures
714
+ bundle exec rspec # 1379 examples, 0 failures
715
715
  bundle exec rubocop # 396 files, 0 offenses
716
716
  ```
717
717
 
@@ -13,15 +13,32 @@ module Legion
13
13
  @running = []
14
14
  @mutex = Mutex.new
15
15
  @max_concurrency = MAX_CONCURRENCY
16
+ @timeout = TIMEOUT
16
17
 
17
18
  class << self
18
- attr_accessor :max_concurrency
19
+ attr_accessor :max_concurrency, :timeout
19
20
 
20
- def configure(max_concurrency: MAX_CONCURRENCY)
21
+ def configure(max_concurrency: MAX_CONCURRENCY, timeout: TIMEOUT)
21
22
  @max_concurrency = max_concurrency
23
+ @timeout = timeout
22
24
  @running = []
23
25
  end
24
26
 
27
+ def configure_from_settings
28
+ mc = begin
29
+ Legion::Settings.dig(:chat, :subagent, :max_concurrency)
30
+ rescue StandardError
31
+ nil
32
+ end
33
+ to = begin
34
+ Legion::Settings.dig(:chat, :subagent, :timeout)
35
+ rescue StandardError
36
+ nil
37
+ end
38
+ @max_concurrency = mc || MAX_CONCURRENCY
39
+ @timeout = to || TIMEOUT
40
+ end
41
+
25
42
  def spawn(task:, model: nil, provider: nil, on_complete: nil)
26
43
  return { error: "Max concurrency reached (#{@max_concurrency}). Wait for a subagent to finish." } if at_capacity?
27
44
 
@@ -54,7 +71,7 @@ module Legion
54
71
  @mutex.synchronize { @running.length >= @max_concurrency }
55
72
  end
56
73
 
57
- def wait_all(timeout: TIMEOUT)
74
+ def wait_all(timeout: @timeout || TIMEOUT)
58
75
  deadline = Time.now + timeout
59
76
  @running.each do |agent|
60
77
  remaining = deadline - Time.now
@@ -45,7 +45,7 @@ module Legion
45
45
  system_prompt = build_system_prompt
46
46
  @session = Chat::Session.new(
47
47
  chat: chat_obj, system_prompt: system_prompt,
48
- budget_usd: options[:max_budget_usd]
48
+ budget_usd: effective_budget
49
49
  )
50
50
  @indicator = Chat::StatusIndicator.new(@session) unless options[:json]
51
51
 
@@ -55,7 +55,7 @@ module Legion
55
55
 
56
56
  setup_notification_bridge
57
57
 
58
- chat_log.info "session started model=#{@session.model_id} incognito=#{options[:incognito]}"
58
+ chat_log.info "session started model=#{@session.model_id} incognito=#{incognito?}"
59
59
  out.banner(version: Legion::VERSION)
60
60
  puts
61
61
  puts out.dim(" Model: #{@session.model_id}")
@@ -80,7 +80,7 @@ module Legion
80
80
 
81
81
  desc 'prompt TEXT', 'Send a single prompt and exit (headless mode)'
82
82
  option :output_format, type: :string, default: 'text', desc: 'Output format: text, json'
83
- option :max_turns, type: :numeric, default: 10, desc: 'Maximum tool-use turns'
83
+ option :max_turns, type: :numeric, desc: 'Maximum tool-use turns (default: 10)'
84
84
  def prompt(text)
85
85
  out = formatter
86
86
  setup_chat_logger
@@ -94,7 +94,7 @@ module Legion
94
94
  system_prompt = build_system_prompt
95
95
  session = Chat::Session.new(
96
96
  chat: chat_obj, system_prompt: system_prompt,
97
- budget_usd: options[:max_budget_usd]
97
+ budget_usd: effective_budget
98
98
  )
99
99
 
100
100
  chat_log.info "headless prompt model=#{session.model_id} length=#{text.length}"
@@ -129,6 +129,24 @@ module Legion
129
129
  end
130
130
 
131
131
  no_commands do
132
+ def chat_setting(*keys)
133
+ Legion::Settings.dig(:chat, *keys)
134
+ rescue StandardError
135
+ nil
136
+ end
137
+
138
+ def incognito?
139
+ options[:incognito] || chat_setting(:incognito) == true
140
+ end
141
+
142
+ def effective_budget
143
+ options[:max_budget_usd] || chat_setting(:max_budget_usd)
144
+ end
145
+
146
+ def effective_max_turns
147
+ options[:max_turns] || chat_setting(:headless, :max_turns) || 10
148
+ end
149
+
132
150
  def formatter
133
151
  @formatter ||= Output::Formatter.new(
134
152
  json: options[:json],
@@ -173,7 +191,12 @@ module Legion
173
191
  end
174
192
 
175
193
  def render_response(text, out)
176
- return text if options[:no_markdown] || options[:no_color]
194
+ markdown_enabled = if options[:no_markdown]
195
+ false
196
+ else
197
+ chat_setting(:markdown) != false
198
+ end
199
+ return text unless markdown_enabled && out.color_enabled
177
200
 
178
201
  require 'legion/cli/chat/markdown_renderer'
179
202
  Chat::MarkdownRenderer.render(text, color: out.color_enabled)
@@ -194,6 +217,8 @@ module Legion
194
217
  require 'legion/cli/chat/permissions'
195
218
  Chat::Permissions.mode = if options[:auto_approve]
196
219
  :auto_approve
220
+ elsif (setting = chat_setting(:permissions))
221
+ setting.to_sym
197
222
  else
198
223
  default
199
224
  end
@@ -201,8 +226,9 @@ module Legion
201
226
 
202
227
  def create_chat
203
228
  opts = {}
204
- opts[:model] = options[:model] if options[:model]
205
- opts[:provider] = options[:provider]&.to_sym if options[:provider]
229
+ opts[:model] = options[:model] || chat_setting(:model)
230
+ opts[:provider] = (options[:provider] || chat_setting(:provider))&.to_sym
231
+ opts.compact!
206
232
 
207
233
  require 'legion/cli/chat/tool_registry'
208
234
  chat = Legion::LLM.chat(**opts)
@@ -217,13 +243,11 @@ module Legion
217
243
  @extra_dirs = options[:add_dir] || []
218
244
  prompt = Chat::Context.to_system_prompt(Dir.pwd, extra_dirs: @extra_dirs)
219
245
 
220
- if options[:personality]
221
- @personality = options[:personality]
222
- case @personality
223
- when 'concise' then prompt += "\n\nBe extremely concise. Short answers, minimal explanation. Code over prose."
224
- when 'verbose' then prompt += "\n\nBe thorough and detailed. Explain your reasoning step by step."
225
- when 'educational' then prompt += "\n\nBe educational. Explain concepts, provide context, teach as you help."
226
- end
246
+ @personality = options[:personality] || chat_setting(:personality)
247
+ case @personality
248
+ when 'concise' then prompt += "\n\nBe extremely concise. Short answers, minimal explanation. Code over prose."
249
+ when 'verbose' then prompt += "\n\nBe thorough and detailed. Explain your reasoning step by step."
250
+ when 'educational' then prompt += "\n\nBe educational. Explain concepts, provide context, teach as you help."
227
251
  end
228
252
 
229
253
  prompt
@@ -1014,7 +1038,7 @@ module Legion
1014
1038
 
1015
1039
  def auto_save_session(out)
1016
1040
  return if @auto_saved
1017
- return if options[:incognito]
1041
+ return if incognito?
1018
1042
  return unless @session
1019
1043
  return if @session.stats[:messages_sent].zero?
1020
1044
 
@@ -7,7 +7,7 @@ require 'net/http'
7
7
  module Legion
8
8
  module CLI
9
9
  module ConfigScaffold
10
- SUBSYSTEMS = %w[transport data cache crypt logging llm].freeze
10
+ SUBSYSTEMS = %w[transport data cache crypt logging llm chat].freeze
11
11
 
12
12
  ENV_DETECTIONS = {
13
13
  'AWS_BEARER_TOKEN_BEDROCK' => { subsystem: 'llm', provider: :bedrock, field: :bearer_token },
@@ -202,6 +202,19 @@ module Legion
202
202
  ollama: { enabled: false, base_url: 'http://localhost:11434' }
203
203
  }
204
204
  } }
205
+ when 'chat'
206
+ { chat: {
207
+ permissions: 'interactive',
208
+ model: nil,
209
+ provider: nil,
210
+ personality: nil,
211
+ markdown: true,
212
+ incognito: false,
213
+ max_budget_usd: nil,
214
+ subagent: { max_concurrency: 3, timeout: 300 },
215
+ headless: { max_turns: 10 },
216
+ notifications: { patterns: [] }
217
+ } }
205
218
  end
206
219
  end
207
220
 
@@ -344,6 +357,19 @@ module Legion
344
357
  ollama: { enabled: false, base_url: 'http://localhost:11434' }
345
358
  }
346
359
  } }
360
+ when 'chat'
361
+ { chat: {
362
+ permissions: 'interactive',
363
+ model: nil,
364
+ provider: nil,
365
+ personality: nil,
366
+ markdown: true,
367
+ incognito: false,
368
+ max_budget_usd: nil,
369
+ subagent: { max_concurrency: 3, timeout: 300 },
370
+ headless: { max_turns: 10 },
371
+ notifications: { patterns: [] }
372
+ } }
347
373
  end
348
374
  end
349
375
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Legion
4
- VERSION = '1.4.60'
4
+ VERSION = '1.4.61'
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.4.60
4
+ version: 1.4.61
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity