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 +4 -4
- data/CHANGELOG.md +7 -0
- data/lib/legion/extensions/agentic/executive/executive_function/helpers/executive_controller.rb +2 -1
- data/lib/legion/extensions/agentic/executive/goal_management/helpers/decomposer.rb +3 -3
- data/lib/legion/extensions/agentic/executive/goal_management/helpers/feedback_listener.rb +22 -2
- data/lib/legion/extensions/agentic/executive/version.rb +1 -1
- data/spec/legion/extensions/agentic/executive/goal_management/helpers/feedback_listener_spec.rb +67 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0ac4e5f2b4c752913d15a4d25240de021a5e7821b2b50be4dd474b1a51ab482f
|
|
4
|
+
data.tar.gz: 9c407b92da789b2f6a11c340b921d772f32fdc5617b968ed5a3dad01c680ce53
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
data/lib/legion/extensions/agentic/executive/executive_function/helpers/executive_controller.rb
CHANGED
|
@@ -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)) + (
|
|
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
|
|
96
|
-
domain: (sg
|
|
97
|
-
priority: (sg
|
|
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
|
data/spec/legion/extensions/agentic/executive/goal_management/helpers/feedback_listener_spec.rb
CHANGED
|
@@ -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
|