lex-agentic-executive 0.2.1 → 0.2.2

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: a52da834db3ad024167228f957b019bfc33bb0a1cc86cc1cb58feabe4aa1bec4
4
- data.tar.gz: 16f3a79dee77a608932cb3b837ee55aac9bc4cfbd596beb42f29582b530c1b54
3
+ metadata.gz: 0ac4e5f2b4c752913d15a4d25240de021a5e7821b2b50be4dd474b1a51ab482f
4
+ data.tar.gz: 9c407b92da789b2f6a11c340b921d772f32fdc5617b968ed5a3dad01c680ce53
5
5
  SHA512:
6
- metadata.gz: e44439bae29cbf8793b0f69ece036bb097051f0434b4d33f4e373f661c5203b51beabb58d9b9f78f17ab9b0fc0041c0565dfbd7d6b11804707e39aa0ef78784a
7
- data.tar.gz: 596a167041a980a4151babe00df9b876a0ebe647929fcea48e0cebe8b9b6704621a5499fcebde0494a8a9c3fff3000b22b2e29ce2da658f481a3ad1350400cd3
6
+ metadata.gz: 558348baa035852ac306d07d992a3fb8c65263e87bad2d359dfa3c6ab611a25c5c6477ab02cd5d6776f81cd5b5e648f6bcd5583a4984705ca52f587e41304f4d
7
+ data.tar.gz: 9c0eb7766904360b3c679c3357fb94006f24841f3eb392e9dcf7690e6282a4d65f67150b4cc204f0c67d7bafd67f1299ce5dcb2eccaed0e5ed50697d8b634fa8
data/CHANGELOG.md CHANGED
@@ -1,6 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.2] - 2026-05-08
4
+ ### Fixed
5
+ - `Decomposer#parse_sub_goals` now accesses JSON sub-goal hashes using string keys (with symbol fallback), preventing LLM-decomposed goals from getting empty content/default domain/0.5 priority.
6
+ - `ExecutiveController#common_ef_level` formula corrected — blends minimum effective capacity (weighted 0.6) with average (weighted 0.4) instead of computing a no-op `avg * 0.6 + avg * 0.4`.
7
+ - `FeedbackListener` tracks event handler references, enabling `stop_listening` and `restart_listening` to prevent duplicate handler accumulation on repeated start calls.
8
+
3
9
  ## [0.2.1] - 2026-05-07
10
+
4
11
  ### Fixed
5
12
  - Goal decomposer LLM strategy now parses native `Legion::LLM.chat` hash responses without requiring a legacy chat session.
6
13
  - Added regression coverage for cognition-domain dispatch to the MindGrowth analyzer.
@@ -72,8 +72,9 @@ module Legion
72
72
 
73
73
  def common_ef_level
74
74
  values = @components.values.map(&:effective_capacity)
75
+ min = values.min
75
76
  avg = values.sum / values.size.to_f
76
- (avg * (1.0 - COMMON_EF_WEIGHT)) + (avg * COMMON_EF_WEIGHT)
77
+ (avg * (1.0 - COMMON_EF_WEIGHT)) + (min * COMMON_EF_WEIGHT)
77
78
  end
78
79
 
79
80
  def can_inhibit?
@@ -92,9 +92,9 @@ module Legion
92
92
 
93
93
  data.map do |sg|
94
94
  {
95
- content: sg[:content].to_s,
96
- domain: (sg[:domain] || domain).to_sym,
97
- priority: (sg[:priority] || 0.5).to_f.clamp(0.0, 1.0)
95
+ content: sg.fetch('content', sg.fetch(:content, '')).to_s,
96
+ domain: sg.fetch('domain', sg.fetch(:domain, domain)).to_sym,
97
+ priority: sg.fetch('priority', sg.fetch(:priority, 0.5)).to_f.clamp(0.0, 1.0)
98
98
  }
99
99
  end
100
100
  rescue StandardError => e
@@ -10,6 +10,7 @@ module Legion
10
10
  def initialize(engine:)
11
11
  @engine = engine
12
12
  @listening = false
13
+ @handlers = []
13
14
  end
14
15
 
15
16
  def handle_task_event(task_id:, status:, result: nil)
@@ -26,7 +27,7 @@ module Legion
26
27
  return if @listening
27
28
  return unless defined?(Legion::Events)
28
29
 
29
- Legion::Events.on('task.completed') do |event|
30
+ handler_completed = Legion::Events.on('task.completed') do |event|
30
31
  handle_task_event(
31
32
  task_id: event[:task_id],
32
33
  status: event[:status] || 'task.completed',
@@ -34,7 +35,7 @@ module Legion
34
35
  )
35
36
  end
36
37
 
37
- Legion::Events.on('task.failed') do |event|
38
+ handler_failed = Legion::Events.on('task.failed') do |event|
38
39
  handle_task_event(
39
40
  task_id: event[:task_id],
40
41
  status: event[:status] || 'task.failed',
@@ -42,9 +43,28 @@ module Legion
42
43
  )
43
44
  end
44
45
 
46
+ @handlers << { event: 'task.completed', block: handler_completed }
47
+ @handlers << { event: 'task.failed', block: handler_failed }
45
48
  @listening = true
46
49
  end
47
50
 
51
+ def stop_listening
52
+ return unless @listening
53
+
54
+ @handlers.each do |entry|
55
+ Legion::Events.off(entry[:event], entry[:block])
56
+ rescue StandardError => e
57
+ log.debug "[feedback_listener] stop_listening cleanup failed: #{e.message}"
58
+ end
59
+ @handlers.clear
60
+ @listening = false
61
+ end
62
+
63
+ def restart_listening
64
+ stop_listening
65
+ start_listening
66
+ end
67
+
48
68
  def listening?
49
69
  @listening
50
70
  end
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module Agentic
6
6
  module Executive
7
- VERSION = '0.2.1'
7
+ VERSION = '0.2.2'
8
8
  end
9
9
  end
10
10
  end
@@ -101,4 +101,71 @@ RSpec.describe Legion::Extensions::Agentic::Executive::GoalManagement::Helpers::
101
101
  expect(listener.listening?).to be false
102
102
  end
103
103
  end
104
+
105
+ describe '#stop_listening' do
106
+ it 'removes registered handlers and resets listening state' do
107
+ events_spy = Class.new do
108
+ attr_reader :registered, :removed
109
+
110
+ def initialize
111
+ @registered = []
112
+ @removed = []
113
+ end
114
+
115
+ def on(event, &block)
116
+ @registered << { event: event, block: block }
117
+ block
118
+ end
119
+
120
+ def off(event, block)
121
+ @removed << { event: event, block: block }
122
+ end
123
+ end.new
124
+ stub_const('Legion::Events', events_spy)
125
+ listener.start_listening
126
+ expect(listener.listening?).to be true
127
+
128
+ listener.stop_listening
129
+ expect(listener.listening?).to be false
130
+ expect(events_spy.removed.size).to eq(2)
131
+ removed_events = events_spy.removed.map { |r| r[:event] }
132
+ expect(removed_events).to include('task.completed')
133
+ expect(removed_events).to include('task.failed')
134
+ end
135
+
136
+ it 'does nothing when not listening' do
137
+ expect { listener.stop_listening }.not_to raise_error
138
+ expect(listener.listening?).to be false
139
+ end
140
+ end
141
+
142
+ describe '#restart_listening' do
143
+ it 'stops and starts listening' do
144
+ events_spy = Class.new do
145
+ attr_reader :registered, :removed
146
+
147
+ def initialize
148
+ @registered = []
149
+ @removed = []
150
+ end
151
+
152
+ def on(event, &block)
153
+ @registered << { event: event, block: block }
154
+ block
155
+ end
156
+
157
+ def off(event, block)
158
+ @removed << { event: event, block: block }
159
+ end
160
+ end.new
161
+ stub_const('Legion::Events', events_spy)
162
+ listener.start_listening
163
+
164
+ listener.restart_listening
165
+ expect(listener.listening?).to be true
166
+ # Original handlers removed, new ones registered
167
+ expect(events_spy.removed.size).to eq(2)
168
+ expect(events_spy.registered.size).to eq(4) # 2 original + 2 new
169
+ end
170
+ end
104
171
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-agentic-executive
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity