lex-agentic-executive 0.2.2 → 0.2.3
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 +5 -0
- data/README.md +1 -1
- data/lib/legion/extensions/agentic/executive/goal_management/helpers/goal_engine.rb +8 -2
- data/lib/legion/extensions/agentic/executive/version.rb +1 -1
- data/lib/legion/extensions/agentic/executive/volition/helpers/intention_stack.rb +6 -0
- data/spec/legion/extensions/agentic/executive/goal_management/helpers/feedback_listener_spec.rb +35 -0
- data/spec/legion/extensions/agentic/executive/volition/helpers/intention_stack_spec.rb +16 -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: 96780b1afcbb05fed19728d4ff7ce759d554c3f546341ae529fc858207c9d7d8
|
|
4
|
+
data.tar.gz: 240e134b5429af9de0ca9783f8632418b6968742b4921f9100acf857762d9909
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 32967c12a79d49227bc65e8ad400ab36aafde6f52b306792df60d5c1655c895bb42c3cb135e9903cd6a4d993538f9ba5bc916763c097e17dd7407730ccec371a
|
|
7
|
+
data.tar.gz: 5c3c763991e6145292134255689f0defb9ee4b565be8098bd1a1d223b5f726d55c3774be7696b6591d8ca1bdf839ca420e7def0db7d22bccdd2717f883a425a7
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.3] - 2026-10-29
|
|
4
|
+
### Fixed
|
|
5
|
+
- Reject unattributed goal feedback and preserve terminal goals and ancestor state when delayed task outcomes arrive.
|
|
6
|
+
- Reclaim completed intention history at capacity without discarding active or suspended commitments.
|
|
7
|
+
|
|
3
8
|
## [0.2.2] - 2026-05-08
|
|
4
9
|
### Fixed
|
|
5
10
|
- `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.
|
data/README.md
CHANGED
|
@@ -213,9 +213,15 @@ module Legion
|
|
|
213
213
|
end
|
|
214
214
|
|
|
215
215
|
def update_from_task_event(task_id:, status:, result: nil)
|
|
216
|
+
return { found: false } if task_id.nil?
|
|
217
|
+
|
|
216
218
|
@mutex.synchronize do
|
|
217
219
|
goal = @goals.values.find { |g| g.task_id == task_id }
|
|
218
220
|
return { found: false } unless goal
|
|
221
|
+
if %i[completed abandoned].include?(goal.status)
|
|
222
|
+
return { found: true, goal_id: goal.id, new_status: goal.status, progress: goal.progress,
|
|
223
|
+
ignored: :terminal_goal }
|
|
224
|
+
end
|
|
219
225
|
|
|
220
226
|
case status
|
|
221
227
|
when 'task.completed'
|
|
@@ -228,8 +234,8 @@ module Legion
|
|
|
228
234
|
when 'task.exception', 'task.failed'
|
|
229
235
|
goal.block!
|
|
230
236
|
persist_goal(goal)
|
|
231
|
-
log.info "[goal_engine] goal status change goal=#{goal.id} transition
|
|
232
|
-
{ found: true, goal_id: goal.id, new_status:
|
|
237
|
+
log.info "[goal_engine] goal status change goal=#{goal.id} transition=#{goal.status}"
|
|
238
|
+
{ found: true, goal_id: goal.id, new_status: goal.status, error: result }
|
|
233
239
|
else
|
|
234
240
|
{ found: true, goal_id: goal.id, unhandled_status: status }
|
|
235
241
|
end
|
|
@@ -15,6 +15,12 @@ module Legion
|
|
|
15
15
|
|
|
16
16
|
def push(intention)
|
|
17
17
|
return :duplicate if duplicate?(intention)
|
|
18
|
+
|
|
19
|
+
if @intentions.size >= Constants::MAX_INTENTIONS
|
|
20
|
+
completed_index = @intentions.index { |entry| entry[:state] == :completed }
|
|
21
|
+
@intentions.delete_at(completed_index) if completed_index
|
|
22
|
+
end
|
|
23
|
+
|
|
18
24
|
return :capacity_full if @intentions.size >= Constants::MAX_INTENTIONS
|
|
19
25
|
|
|
20
26
|
@intentions << intention
|
data/spec/legion/extensions/agentic/executive/goal_management/helpers/feedback_listener_spec.rb
CHANGED
|
@@ -40,6 +40,41 @@ RSpec.describe Legion::Extensions::Agentic::Executive::GoalManagement::Helpers::
|
|
|
40
40
|
result = listener.handle_task_event(task_id: 'task-123', status: 'task.failed', result: 'timeout')
|
|
41
41
|
expect(result[:error]).to eq('timeout')
|
|
42
42
|
end
|
|
43
|
+
|
|
44
|
+
it 'does not attribute an event without a task ID to an undispatched goal' do
|
|
45
|
+
undispatched = engine.add_goal(content: 'awaiting authorized dispatch')[:goal][:id]
|
|
46
|
+
engine.activate_goal(goal_id: undispatched)
|
|
47
|
+
|
|
48
|
+
result = listener.handle_task_event(task_id: nil, status: 'task.completed')
|
|
49
|
+
|
|
50
|
+
expect(result).to eq(found: false)
|
|
51
|
+
expect(engine.goal_tree(goal_id: undispatched)[:tree]).to include(status: :active, progress: 0.0)
|
|
52
|
+
expect(engine.goal_tree(goal_id: @goal_id)[:tree]).to include(status: :active, progress: 0.0)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'preserves completion when a delayed failure arrives for the same task' do
|
|
56
|
+
listener.handle_task_event(task_id: 'task-123', status: 'task.completed')
|
|
57
|
+
|
|
58
|
+
result = listener.handle_task_event(task_id: 'task-123', status: 'task.failed', result: 'late failure')
|
|
59
|
+
|
|
60
|
+
expect(result).to include(new_status: :completed, ignored: :terminal_goal)
|
|
61
|
+
expect(engine.goal_tree(goal_id: @goal_id)[:tree]).to include(status: :completed, progress: 1.0)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'does not credit an abandoned child or its parent for a late completion' do
|
|
65
|
+
parent = engine.add_goal(content: 'parent goal')[:goal][:id]
|
|
66
|
+
engine.activate_goal(goal_id: parent)
|
|
67
|
+
child = engine.add_goal(content: 'cancelled work', parent_id: parent)[:goal][:id]
|
|
68
|
+
engine.activate_goal(goal_id: child)
|
|
69
|
+
engine.assign_task_to_goal(goal_id: child, task_id: 'cancelled-task', runner_mapping: 'TestRunner')
|
|
70
|
+
engine.abandon_goal(goal_id: child)
|
|
71
|
+
before = engine.goal_tree(goal_id: parent)[:tree]
|
|
72
|
+
|
|
73
|
+
result = listener.handle_task_event(task_id: 'cancelled-task', status: 'task.completed')
|
|
74
|
+
|
|
75
|
+
expect(result).to include(new_status: :abandoned, ignored: :terminal_goal)
|
|
76
|
+
expect(engine.goal_tree(goal_id: parent)[:tree]).to eq(before)
|
|
77
|
+
end
|
|
43
78
|
end
|
|
44
79
|
|
|
45
80
|
describe '#start_listening' do
|
|
@@ -27,6 +27,22 @@ RSpec.describe Legion::Extensions::Agentic::Executive::Volition::Helpers::Intent
|
|
|
27
27
|
expect(stack.push(make_intention(goal: 'overflow'))).to eq(:capacity_full)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
it 'reclaims completed history at capacity without losing pending commitments' do
|
|
31
|
+
limit = Legion::Extensions::Agentic::Executive::Volition::Helpers::Constants::MAX_INTENTIONS
|
|
32
|
+
intentions = Array.new(limit) { |i| make_intention(goal: "goal #{i}") }
|
|
33
|
+
intentions.each { |intention| stack.push(intention) }
|
|
34
|
+
stack.complete(intentions.first[:intention_id])
|
|
35
|
+
stack.suspend(intentions.last[:intention_id])
|
|
36
|
+
replacement = make_intention(goal: 'next commitment')
|
|
37
|
+
|
|
38
|
+
expect(stack.push(replacement)).to eq(:pushed)
|
|
39
|
+
expect(stack.size).to eq(limit)
|
|
40
|
+
expect(stack.active.map { |intention| intention[:goal] })
|
|
41
|
+
.to contain_exactly(*intentions[1...-1].map { |intention| intention[:goal] }, 'next commitment')
|
|
42
|
+
expect(stack.resume(intentions.last[:intention_id])).to eq(:resumed)
|
|
43
|
+
expect(stack.push(make_intention(goal: 'overflow'))).to eq(:capacity_full)
|
|
44
|
+
end
|
|
45
|
+
|
|
30
46
|
it 'sorts by salience descending' do
|
|
31
47
|
stack.push(make_intention(goal: 'low', salience: 0.3))
|
|
32
48
|
stack.push(make_intention(goal: 'high', salience: 0.9))
|