lex-agentic-attention 0.1.6 → 0.1.8

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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/README.md +32 -3
  4. data/lib/legion/extensions/agentic/attention/arousal/actors/update.rb +45 -0
  5. data/lib/legion/extensions/agentic/attention/blink/actors/decay.rb +45 -0
  6. data/lib/legion/extensions/agentic/attention/blink/runners/attentional_blink.rb +7 -0
  7. data/lib/legion/extensions/agentic/attention/economy/runners/attention_economy.rb +2 -2
  8. data/lib/legion/extensions/agentic/attention/feature_binding/actors/decay.rb +1 -1
  9. data/lib/legion/extensions/agentic/attention/focus/helpers/focus.rb +2 -1
  10. data/lib/legion/extensions/agentic/attention/focus/runners/attention.rb +2 -0
  11. data/lib/legion/extensions/agentic/attention/lens/helpers/constants.rb +5 -5
  12. data/lib/legion/extensions/agentic/attention/lens/helpers/lens_engine.rb +1 -2
  13. data/lib/legion/extensions/agentic/attention/regulation/actors/update.rb +45 -0
  14. data/lib/legion/extensions/agentic/attention/salience/actors/compute.rb +45 -0
  15. data/lib/legion/extensions/agentic/attention/schema/actors/decay.rb +1 -1
  16. data/lib/legion/extensions/agentic/attention/signal_detection/actors/update.rb +45 -0
  17. data/lib/legion/extensions/agentic/attention/spotlight/runners/attention_spotlight.rb +3 -3
  18. data/lib/legion/extensions/agentic/attention/synesthesia/helpers/synesthesia_engine.rb +2 -4
  19. data/lib/legion/extensions/agentic/attention/version.rb +1 -1
  20. data/spec/legion/extensions/agentic/attention/arousal/actors/update_spec.rb +41 -0
  21. data/spec/legion/extensions/agentic/attention/blink/actors/decay_spec.rb +41 -0
  22. data/spec/legion/extensions/agentic/attention/blink/runners/attentional_blink_spec.rb +23 -0
  23. data/spec/legion/extensions/agentic/attention/economy/client_spec.rb +3 -3
  24. data/spec/legion/extensions/agentic/attention/economy/runners/attention_economy_spec.rb +3 -3
  25. data/spec/legion/extensions/agentic/attention/focus/runners/attention_spec.rb +33 -36
  26. data/spec/legion/extensions/agentic/attention/regulation/actors/update_spec.rb +41 -0
  27. data/spec/legion/extensions/agentic/attention/salience/actors/compute_spec.rb +41 -0
  28. data/spec/legion/extensions/agentic/attention/signal_detection/actors/update_spec.rb +41 -0
  29. data/spec/legion/extensions/agentic/attention/spotlight/client_spec.rb +1 -1
  30. data/spec/legion/extensions/agentic/attention/spotlight/runners/attention_spotlight_spec.rb +4 -4
  31. data/spec/spec_helper.rb +4 -0
  32. metadata +11 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc820ae52c879ee0a59e6fd9551eb037c9a00214c8096a545b7e63a2b95c072b
4
- data.tar.gz: 4c5d0b898ed697915a97c9ac95d5addf0a186600aeea84d11cc20bc5f03f17a4
3
+ metadata.gz: 9e6357375a251617aae1b7b3c415374cf2ccaf930f0cec48e0b0adc2db4310e5
4
+ data.tar.gz: fb2bf32e027a564c17d0914a3496a65d3d8c844205cd6c09326ea24783f6dd20
5
5
  SHA512:
6
- metadata.gz: f0d55e482146cd4c07bbbdfd6e53bb4268e625c509d321b0cef7fa0bdf506038315fc58775379b465bcace069fc7d7563f0acaa98b3ba348cf55f154b013d430
7
- data.tar.gz: 232af3b13aa8656873b6e8b2a959293080acf7ba147268fe4fdf0e2b3c1916498a7edf915481f5c80738c3b4f843f5dee2bdab9b31d378bf5943cfab2be40427
6
+ metadata.gz: 93b6915bcd364e1c83b1ab28e032c1d557d8c297a5efe148658fe9a804d0512b87ac5a1a227ca3b2fb01c2bfa2adfbcc014ad93e701e4ee60cd030ff8268f35b
7
+ data.tar.gz: 7ba33757d9c2ae8f2a16a777dbc7c6738d56aa3793906e3fbba7643428f916a43ee9b96cac8cca9f632283b8c0dc298218a6366c2ffea745564e8cf65e265071
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.8] - 2026-10-29
4
+ ### Fixed
5
+ - Preserve principal, channel, and frame identity when deduplicating attention signals.
6
+ - Return retained spotlight and peripheral signals in priority order, including batches below spotlight capacity.
7
+
8
+ ## [0.1.7] - 2026-04-22
9
+ ### Fixed
10
+ - Renamed Economy `attention_status` to `attention_economy_status` to resolve method collision with Focus
11
+ - Renamed Spotlight `release_focus` to `release_spotlight` to resolve method collision with Focus
12
+ ### Added
13
+ - 5 new maintenance actors: Salience::Compute (12s), Arousal::Update (30s), SignalDetection::Update (60s), Regulation::Update (60s), Blink::Decay (15s)
14
+ - `decay_blink` method for attentional blink recovery
15
+
3
16
  ## [0.1.6] - 2026-04-15
4
17
  ### Changed
5
18
  - Set `mcp_tools?`, `mcp_tools_deferred?`, and `transport_required?` to `false` — internal cognitive pipeline extension
data/README.md CHANGED
@@ -5,7 +5,7 @@ Domain consolidation gem for attention, perception, and signal processing. Bundl
5
5
  ## Overview
6
6
 
7
7
  **Gem**: `lex-agentic-attention`
8
- **Version**: 0.1.6
8
+ **Version**: 0.1.8
9
9
  **Namespace**: `Legion::Extensions::Agentic::Attention`
10
10
 
11
11
  ## Sub-Modules
@@ -39,8 +39,15 @@ Domain consolidation gem for attention, perception, and signal processing. Bundl
39
39
 
40
40
  ## Actors
41
41
 
42
- - `Attention::FeatureBinding::Actors::Decay` — interval actor, decays bound feature groups
43
- - `Attention::Schema::Actors::Decay` — interval actor, decays attention schema entries
42
+ | Actor | Interval | What It Does |
43
+ |-------|----------|--------------|
44
+ | `Arousal::Actor::Update` | Every 30s | Updates arousal level using Yerkes-Dodson model |
45
+ | `Blink::Actor::Decay` | Every 15s | Decays attentional blink suppression window |
46
+ | `FeatureBinding::Actors::Decay` | interval | Decays bound feature groups |
47
+ | `Regulation::Actor::Update` | Every 60s | Applies top-down/bottom-up regulation updates |
48
+ | `Salience::Actor::Compute` | Every 12s | Recomputes salience map from all eight sources |
49
+ | `Schema::Actor::Decay` | Every 30s | Decays attention schema entries |
50
+ | `SignalDetection::Actor::Update` | Every 60s | Updates SDT sensitivity (d') and response bias (beta) |
44
51
 
45
52
  ## Installation
46
53
 
@@ -48,6 +55,28 @@ Domain consolidation gem for attention, perception, and signal processing. Bundl
48
55
  gem 'lex-agentic-attention'
49
56
  ```
50
57
 
58
+ ## Usage
59
+
60
+ ```ruby
61
+ require 'legion/extensions/agentic/attention'
62
+
63
+ # Filter incoming signals through the attention focus system
64
+ client = Legion::Extensions::Agentic::Attention::Focus::Client.new
65
+ result = client.filter_signals(signals: incoming, active_wonders: goals)
66
+ # => { filtered: [...], spotlight: 3, peripheral: 2, background: 5, dropped: 1 }
67
+
68
+ # Check attention economy status
69
+ economy = Legion::Extensions::Agentic::Attention::Economy::Client.new
70
+ economy.attention_economy_status
71
+ # => { total_budget:, spent:, available:, utilization:, demand_count:, ... }
72
+
73
+ # Focus the spotlight on a specific target
74
+ spotlight = Legion::Extensions::Agentic::Attention::Spotlight::Client.new
75
+ spotlight.register_target(label: 'critical_alert', domain: :safety, salience: 0.9, relevance: 0.9)
76
+ spotlight.focus_spotlight(target_id: '<id>')
77
+ spotlight.release_spotlight
78
+ ```
79
+
51
80
  ## Development
52
81
 
53
82
  ```bash
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/actors/every'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Agentic
8
+ module Attention
9
+ module Arousal
10
+ module Actor
11
+ class Update < Legion::Extensions::Actors::Every
12
+ def runner_class
13
+ Legion::Extensions::Agentic::Attention::Arousal::Runners::Arousal
14
+ end
15
+
16
+ def runner_function
17
+ 'update_arousal'
18
+ end
19
+
20
+ def time
21
+ 30
22
+ end
23
+
24
+ def run_now?
25
+ false
26
+ end
27
+
28
+ def use_runner?
29
+ false
30
+ end
31
+
32
+ def check_subtask?
33
+ false
34
+ end
35
+
36
+ def generate_task?
37
+ false
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/actors/every'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Agentic
8
+ module Attention
9
+ module Blink
10
+ module Actor
11
+ class Decay < Legion::Extensions::Actors::Every
12
+ def runner_class
13
+ Legion::Extensions::Agentic::Attention::Blink::Runners::AttentionalBlink
14
+ end
15
+
16
+ def runner_function
17
+ 'decay_blink'
18
+ end
19
+
20
+ def time
21
+ 15
22
+ end
23
+
24
+ def run_now?
25
+ false
26
+ end
27
+
28
+ def use_runner?
29
+ false
30
+ end
31
+
32
+ def check_subtask?
33
+ false
34
+ end
35
+
36
+ def generate_task?
37
+ false
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -66,6 +66,13 @@ module Legion
66
66
  def attentional_blink_stats(**)
67
67
  engine.to_h
68
68
  end
69
+
70
+ def decay_blink(**)
71
+ active = engine.blink_active?
72
+ level = engine.recovery_level
73
+ log.debug("[attentional_blink] decay tick: active=#{active} recovery=#{level.round(3)}")
74
+ { success: true, blink_active: active, recovery_level: level }
75
+ end
69
76
  end
70
77
  end
71
78
  end
@@ -66,9 +66,9 @@ module Legion
66
66
  result
67
67
  end
68
68
 
69
- def attention_status(**)
69
+ def attention_economy_status(**)
70
70
  report = attention_budget.budget_report
71
- log.debug("[attention] status: utilization=#{report[:utilization]} demands=#{report[:demand_count]}")
71
+ log.debug("[attention] economy status: utilization=#{report[:utilization]} demands=#{report[:demand_count]}")
72
72
  report
73
73
  end
74
74
 
@@ -8,7 +8,7 @@ module Legion
8
8
  module Attention
9
9
  module FeatureBinding
10
10
  module Actor
11
- class Decay < Legion::Extensions::Actors::Every # rubocop:disable Legion/Extension/EveryActorRequiresTime
11
+ class Decay < Legion::Extensions::Actors::Every
12
12
  def runner_class
13
13
  Legion::Extensions::Agentic::Attention::FeatureBinding::Runners::FeatureBinding
14
14
  end
@@ -67,7 +67,8 @@ module Legion
67
67
  def signal_identity(signal)
68
68
  return signal.object_id unless signal.is_a?(Hash)
69
69
 
70
- [signal[:source_type], signal[:domain], signal[:value]].hash
70
+ [signal[:source_type], signal[:domain], signal[:principal_id],
71
+ signal[:channel_id], signal[:frame_id], signal[:value]]
71
72
  end
72
73
  end
73
74
  end
@@ -103,12 +103,14 @@ module Legion
103
103
  def enforce_capacity(categorized)
104
104
  capacity = Helpers::Constants::ATTENTIONAL_CAPACITY
105
105
  spotlight = categorized[:spotlight].sort_by { |s| -(s[:attention_score] || 0) }
106
+ categorized[:spotlight] = spotlight
106
107
 
107
108
  if spotlight.size > capacity
108
109
  overflow = spotlight[capacity..]
109
110
  categorized[:spotlight] = spotlight.first(capacity)
110
111
  categorized[:peripheral].concat(overflow)
111
112
  end
113
+ categorized[:peripheral].sort_by! { |s| -(s[:attention_score] || 0) }
112
114
 
113
115
  categorized
114
116
  end
@@ -14,11 +14,11 @@ module Legion
14
14
  MAX_LENSES = 8
15
15
 
16
16
  CLARITY_LABELS = [
17
- [0.0..0.2, :opaque],
18
- [0.2..0.4, :foggy],
19
- [0.4..0.6, :hazy],
20
- [0.6..0.8, :clear],
21
- [0.8..1.0, :crystal]
17
+ [0.0..0.2, :opaque],
18
+ [0.2..0.4, :foggy],
19
+ [0.4..0.6, :hazy],
20
+ [0.6..0.8, :clear],
21
+ [0.8..1.0, :crystal]
22
22
  ].freeze
23
23
 
24
24
  MAGNIFICATION_LABELS = [
@@ -48,13 +48,12 @@ module Legion
48
48
  raise ArgumentError, "stack not found: #{stack_id}" unless stack
49
49
 
50
50
  result = stack.view_through(content)
51
- # rubocop:disable Legion/HelperMigration/DirectLogging
51
+ # rubocop:disable-next Legion/HelperMigration/DirectLogging
52
52
  Legion::Logging.debug("[cognitive_lens] view_through stack=#{stack_id} " \
53
53
  "perceived=#{result[:perceived].round(4)} " \
54
54
  "magnification=#{result[:combined_magnification].round(2)} " \
55
55
  "distortion=#{result[:combined_distortion].round(2)} " \
56
56
  "clarity=#{result[:stack_clarity].round(2)}")
57
- # rubocop:enable Legion/HelperMigration/DirectLogging
58
57
  result
59
58
  end
60
59
 
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/actors/every'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Agentic
8
+ module Attention
9
+ module Regulation
10
+ module Actor
11
+ class Update < Legion::Extensions::Actors::Every
12
+ def runner_class
13
+ Legion::Extensions::Agentic::Attention::Regulation::Runners::AttentionRegulation
14
+ end
15
+
16
+ def runner_function
17
+ 'update_attention'
18
+ end
19
+
20
+ def time
21
+ 60
22
+ end
23
+
24
+ def run_now?
25
+ false
26
+ end
27
+
28
+ def use_runner?
29
+ false
30
+ end
31
+
32
+ def check_subtask?
33
+ false
34
+ end
35
+
36
+ def generate_task?
37
+ false
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/actors/every'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Agentic
8
+ module Attention
9
+ module Salience
10
+ module Actor
11
+ class Compute < Legion::Extensions::Actors::Every
12
+ def runner_class
13
+ Legion::Extensions::Agentic::Attention::Salience::Runners::Salience
14
+ end
15
+
16
+ def runner_function
17
+ 'compute_salience'
18
+ end
19
+
20
+ def time
21
+ 12
22
+ end
23
+
24
+ def run_now?
25
+ false
26
+ end
27
+
28
+ def use_runner?
29
+ false
30
+ end
31
+
32
+ def check_subtask?
33
+ false
34
+ end
35
+
36
+ def generate_task?
37
+ false
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -8,7 +8,7 @@ module Legion
8
8
  module Attention
9
9
  module Schema
10
10
  module Actor
11
- class Decay < Legion::Extensions::Actors::Every # rubocop:disable Legion/Extension/EveryActorRequiresTime
11
+ class Decay < Legion::Extensions::Actors::Every
12
12
  def runner_class
13
13
  Legion::Extensions::Agentic::Attention::Schema::Runners::AttentionSchema
14
14
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/actors/every'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Agentic
8
+ module Attention
9
+ module SignalDetection
10
+ module Actor
11
+ class Update < Legion::Extensions::Actors::Every
12
+ def runner_class
13
+ Legion::Extensions::Agentic::Attention::SignalDetection::Runners::SignalDetection
14
+ end
15
+
16
+ def runner_function
17
+ 'update_signal_detection'
18
+ end
19
+
20
+ def time
21
+ 60
22
+ end
23
+
24
+ def run_now?
25
+ false
26
+ end
27
+
28
+ def use_runner?
29
+ false
30
+ end
31
+
32
+ def check_subtask?
33
+ false
34
+ end
35
+
36
+ def generate_task?
37
+ false
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -74,12 +74,12 @@ module Legion
74
74
  { success: false, error: e.message }
75
75
  end
76
76
 
77
- def release_focus(**)
78
- log.debug('[attention_spotlight] releasing focus')
77
+ def release_spotlight(**)
78
+ log.debug('[attention_spotlight] releasing spotlight')
79
79
  result = engine.release_focus
80
80
  { success: true, **result }
81
81
  rescue StandardError => e
82
- log.error("[attention_spotlight] release_focus error: #{e.message}")
82
+ log.error("[attention_spotlight] release_spotlight error: #{e.message}")
83
83
  { success: false, error: e.message }
84
84
  end
85
85
 
@@ -32,10 +32,9 @@ module Legion
32
32
  @mappings[mapping.id] = mapping
33
33
  prune_mappings! if @mappings.size > MAX_MAPPINGS
34
34
 
35
- # rubocop:disable Legion/HelperMigration/DirectLogging
35
+ # rubocop:disable-next Legion/HelperMigration/DirectLogging
36
36
  Legion::Logging.debug("[cognitive_synesthesia] mapping registered id=#{mapping.id[0..7]} " \
37
37
  "#{source_modality}->#{target_modality} strength=#{strength.round(2)}")
38
- # rubocop:enable Legion/HelperMigration/DirectLogging
39
38
 
40
39
  { success: true, mapping_id: mapping.id, source_modality: source_modality,
41
40
  target_modality: target_modality }
@@ -137,10 +136,9 @@ module Legion
137
136
  @events << event
138
137
  @events.shift while @events.size > MAX_EVENTS
139
138
 
140
- # rubocop:disable Legion/HelperMigration/DirectLogging
139
+ # rubocop:disable-next Legion/HelperMigration/DirectLogging
141
140
  Legion::Logging.debug("[cognitive_synesthesia] event fired id=#{event.id[0..7]} " \
142
141
  "mapping=#{mapping.id[0..7]} intensity=#{intensity.round(2)}")
143
- # rubocop:enable Legion/HelperMigration/DirectLogging
144
142
  event
145
143
  end
146
144
 
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module Agentic
6
6
  module Attention
7
- VERSION = '0.1.6'
7
+ VERSION = '0.1.8'
8
8
  end
9
9
  end
10
10
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/attention/arousal/actors/update'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Attention::Arousal::Actor::Update do
6
+ subject(:actor) { described_class.new }
7
+
8
+ describe '#runner_class' do
9
+ it 'points to the Arousal runner' do
10
+ expect(actor.runner_class).to eq Legion::Extensions::Agentic::Attention::Arousal::Runners::Arousal
11
+ end
12
+ end
13
+
14
+ describe '#runner_function' do
15
+ it 'is update_arousal' do
16
+ expect(actor.runner_function).to eq 'update_arousal'
17
+ end
18
+ end
19
+
20
+ describe '#time' do
21
+ it 'runs every 30 seconds' do
22
+ expect(actor.time).to eq 30
23
+ end
24
+ end
25
+
26
+ describe '#run_now?' do
27
+ it { expect(actor.run_now?).to be false }
28
+ end
29
+
30
+ describe '#use_runner?' do
31
+ it { expect(actor.use_runner?).to be false }
32
+ end
33
+
34
+ describe '#check_subtask?' do
35
+ it { expect(actor.check_subtask?).to be false }
36
+ end
37
+
38
+ describe '#generate_task?' do
39
+ it { expect(actor.generate_task?).to be false }
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/attention/blink/actors/decay'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Attention::Blink::Actor::Decay do
6
+ subject(:actor) { described_class.new }
7
+
8
+ describe '#runner_class' do
9
+ it 'points to the AttentionalBlink runner' do
10
+ expect(actor.runner_class).to eq Legion::Extensions::Agentic::Attention::Blink::Runners::AttentionalBlink
11
+ end
12
+ end
13
+
14
+ describe '#runner_function' do
15
+ it 'is decay_blink' do
16
+ expect(actor.runner_function).to eq 'decay_blink'
17
+ end
18
+ end
19
+
20
+ describe '#time' do
21
+ it 'runs every 15 seconds' do
22
+ expect(actor.time).to eq 15
23
+ end
24
+ end
25
+
26
+ describe '#run_now?' do
27
+ it { expect(actor.run_now?).to be false }
28
+ end
29
+
30
+ describe '#use_runner?' do
31
+ it { expect(actor.use_runner?).to be false }
32
+ end
33
+
34
+ describe '#check_subtask?' do
35
+ it { expect(actor.check_subtask?).to be false }
36
+ end
37
+
38
+ describe '#generate_task?' do
39
+ it { expect(actor.generate_task?).to be false }
40
+ end
41
+ end
@@ -74,4 +74,27 @@ RSpec.describe Legion::Extensions::Agentic::Attention::Blink::Runners::Attention
74
74
  expect(result).to include(:total_stimuli, :total_blinks, :miss_rate)
75
75
  end
76
76
  end
77
+
78
+ describe '#decay_blink' do
79
+ it 'returns success true' do
80
+ result = runner.decay_blink
81
+ expect(result[:success]).to be true
82
+ end
83
+
84
+ it 'returns blink_active status' do
85
+ result = runner.decay_blink
86
+ expect(result).to have_key(:blink_active)
87
+ end
88
+
89
+ it 'returns recovery_level' do
90
+ result = runner.decay_blink
91
+ expect(result).to have_key(:recovery_level)
92
+ end
93
+
94
+ it 'reports recovery complete when no blink is active' do
95
+ result = runner.decay_blink
96
+ expect(result[:blink_active]).to be false
97
+ expect(result[:recovery_level]).to eq(1.0)
98
+ end
99
+ end
77
100
  end
@@ -13,18 +13,18 @@ RSpec.describe Legion::Extensions::Agentic::Attention::Economy::Client do
13
13
  expect(client).to respond_to(:prioritized_demands)
14
14
  expect(client).to respond_to(:best_roi_demands)
15
15
  expect(client).to respond_to(:rebalance_budget)
16
- expect(client).to respond_to(:attention_status)
16
+ expect(client).to respond_to(:attention_economy_status)
17
17
  expect(client).to respond_to(:attention_snapshot)
18
18
  end
19
19
 
20
20
  it 'starts with empty demands' do
21
- expect(client.attention_status[:demand_count]).to eq(0)
21
+ expect(client.attention_economy_status[:demand_count]).to eq(0)
22
22
  end
23
23
 
24
24
  it 'maintains isolated state per instance' do
25
25
  c1 = described_class.new
26
26
  c2 = described_class.new
27
27
  c1.add_demand(label: 'solo', demand_type: :task, priority: 0.5, cost: 0.1)
28
- expect(c2.attention_status[:demand_count]).to eq(0)
28
+ expect(c2.attention_economy_status[:demand_count]).to eq(0)
29
29
  end
30
30
  end
@@ -133,9 +133,9 @@ RSpec.describe Legion::Extensions::Agentic::Attention::Economy::Runners::Attenti
133
133
  end
134
134
  end
135
135
 
136
- describe '#attention_status' do
136
+ describe '#attention_economy_status' do
137
137
  it 'returns budget report keys' do
138
- result = client.attention_status
138
+ result = client.attention_economy_status
139
139
  %i[total_budget spent available utilization demand_count].each do |key|
140
140
  expect(result).to have_key(key)
141
141
  end
@@ -143,7 +143,7 @@ RSpec.describe Legion::Extensions::Agentic::Attention::Economy::Runners::Attenti
143
143
 
144
144
  it 'reflects demand count' do
145
145
  add_task
146
- expect(client.attention_status[:demand_count]).to eq(1)
146
+ expect(client.attention_economy_status[:demand_count]).to eq(1)
147
147
  end
148
148
  end
149
149
 
@@ -26,35 +26,48 @@ RSpec.describe Legion::Extensions::Agentic::Attention::Focus::Runners::Attention
26
26
  expect(result[:filtered].size).to be >= 1
27
27
  end
28
28
 
29
- it 'drops low-salience signals' do
30
- signals = [{ salience: 0.01, domain: :heartbeat, novelty: 0.0 }]
29
+ it 'deduplicates identical signals' do
30
+ signals = [
31
+ { salience: 0.8, domain: :terraform, source_type: :event, value: 'deploy' },
32
+ { salience: 0.8, domain: :terraform, source_type: :event, value: 'deploy' }
33
+ ]
31
34
  result = client.filter_signals(signals: signals)
32
- expect(result[:dropped]).to be >= 0
33
- expect(result[:filtered].size).to be <= 1
35
+ expect(result[:spotlight] + result[:peripheral] + result[:background]).to be <= 1
34
36
  end
35
37
 
36
- it 'boosts signals matching active wonders' do
37
- signals = [{ salience: 0.3, domain: :terraform, novelty: 0.3 }]
38
- wonders = [{ domain: :terraform, salience: 0.9 }]
38
+ it 'preserves identical requests from different principals and frames' do
39
+ request = { salience: 0.9, source_type: :human_direct, value: 'continue', channel_id: :cli }
40
+ signals = [
41
+ request.merge(principal_id: 'alice', frame_id: 'first'),
42
+ request.merge(principal_id: 'bob', frame_id: 'second'),
43
+ request.merge(principal_id: 'alice', frame_id: 'third')
44
+ ]
39
45
 
40
- without = client.filter_signals(signals: signals)
41
- # New client to avoid habituation carryover
42
- fresh = Legion::Extensions::Agentic::Attention::Focus::Client.new
43
- with_wonders = fresh.filter_signals(signals: signals, active_wonders: wonders)
46
+ result = client.filter_signals(signals: signals + [signals.first.dup])
44
47
 
45
- # With wonder boost, the signal should score higher
46
- with_total = with_wonders[:spotlight] + with_wonders[:peripheral]
47
- without_total = without[:spotlight] + without[:peripheral]
48
- expect(with_total).to be >= without_total
48
+ expect(result[:filtered].map { |signal| signal[:frame_id] }).to contain_exactly('first', 'second', 'third')
49
49
  end
50
50
 
51
- it 'deduplicates identical signals' do
51
+ it 'keeps requests from separate channels distinct when no frame identity is available' do
52
+ request = { salience: 0.9, source_type: :human_direct, value: 'continue' }
53
+ result = client.filter_signals(signals: [request.merge(channel_id: :cli), request.merge(channel_id: :slack)])
54
+
55
+ expect(result[:filtered].map { |signal| signal[:channel_id] }).to contain_exactly(:cli, :slack)
56
+ end
57
+
58
+ it 'prioritizes signals within both retained tiers below the capacity limit' do
52
59
  signals = [
53
- { salience: 0.8, domain: :terraform, source_type: :event, value: 'deploy' },
54
- { salience: 0.8, domain: :terraform, source_type: :event, value: 'deploy' }
60
+ { value: 'spotlight-low', salience: 0.8, novelty: 0.8, domain: :deploy },
61
+ { value: 'spotlight-high', salience: 1.0, novelty: 1.0, domain: :incident },
62
+ { value: 'peripheral-low', salience: 0.7, novelty: 0.5, domain: :status },
63
+ { value: 'peripheral-high', salience: 0.9, novelty: 0.5, domain: :review }
55
64
  ]
56
- result = client.filter_signals(signals: signals)
57
- expect(result[:spotlight] + result[:peripheral] + result[:background]).to be <= 1
65
+ wonders = [{ domain: :deploy, salience: 1.0 }, { domain: :incident, salience: 1.0 }]
66
+
67
+ result = client.filter_signals(signals: signals, active_wonders: wonders)
68
+
69
+ expect(result[:filtered].map { |signal| signal[:value] })
70
+ .to eq(%w[spotlight-high spotlight-low peripheral-high peripheral-low])
58
71
  end
59
72
 
60
73
  it 'enforces attentional capacity' do
@@ -62,22 +75,6 @@ RSpec.describe Legion::Extensions::Agentic::Attention::Focus::Runners::Attention
62
75
  result = client.filter_signals(signals: signals)
63
76
  expect(result[:spotlight]).to be <= Legion::Extensions::Agentic::Attention::Focus::Helpers::Constants::ATTENTIONAL_CAPACITY
64
77
  end
65
-
66
- it 'habituates repeated domain signals' do
67
- first_client = Legion::Extensions::Agentic::Attention::Focus::Client.new
68
- signals = [{ salience: 0.6, domain: :terraform, novelty: 0.3 }]
69
-
70
- first_result = first_client.filter_signals(signals: signals)
71
- # Record same domain multiple times to build habituation
72
- 10.times { first_client.filter_signals(signals: signals) }
73
- later_result = first_client.filter_signals(signals: signals)
74
-
75
- first_scores = first_result[:filtered].map { |s| s[:attention_score] || 0 }
76
- later_scores = later_result[:filtered].map { |s| s[:attention_score] || 0 }
77
-
78
- # Later scores should be lower due to habituation (if signal still passes)
79
- expect(later_scores.max).to be <= first_scores.max if first_scores.any? && later_scores.any?
80
- end
81
78
  end
82
79
 
83
80
  describe '#attention_status' do
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/attention/regulation/actors/update'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Attention::Regulation::Actor::Update do
6
+ subject(:actor) { described_class.new }
7
+
8
+ describe '#runner_class' do
9
+ it 'points to the AttentionRegulation runner' do
10
+ expect(actor.runner_class).to eq Legion::Extensions::Agentic::Attention::Regulation::Runners::AttentionRegulation
11
+ end
12
+ end
13
+
14
+ describe '#runner_function' do
15
+ it 'is update_attention' do
16
+ expect(actor.runner_function).to eq 'update_attention'
17
+ end
18
+ end
19
+
20
+ describe '#time' do
21
+ it 'runs every 60 seconds' do
22
+ expect(actor.time).to eq 60
23
+ end
24
+ end
25
+
26
+ describe '#run_now?' do
27
+ it { expect(actor.run_now?).to be false }
28
+ end
29
+
30
+ describe '#use_runner?' do
31
+ it { expect(actor.use_runner?).to be false }
32
+ end
33
+
34
+ describe '#check_subtask?' do
35
+ it { expect(actor.check_subtask?).to be false }
36
+ end
37
+
38
+ describe '#generate_task?' do
39
+ it { expect(actor.generate_task?).to be false }
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/attention/salience/actors/compute'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Attention::Salience::Actor::Compute do
6
+ subject(:actor) { described_class.new }
7
+
8
+ describe '#runner_class' do
9
+ it 'points to the Salience runner' do
10
+ expect(actor.runner_class).to eq Legion::Extensions::Agentic::Attention::Salience::Runners::Salience
11
+ end
12
+ end
13
+
14
+ describe '#runner_function' do
15
+ it 'is compute_salience' do
16
+ expect(actor.runner_function).to eq 'compute_salience'
17
+ end
18
+ end
19
+
20
+ describe '#time' do
21
+ it 'runs every 12 seconds' do
22
+ expect(actor.time).to eq 12
23
+ end
24
+ end
25
+
26
+ describe '#run_now?' do
27
+ it { expect(actor.run_now?).to be false }
28
+ end
29
+
30
+ describe '#use_runner?' do
31
+ it { expect(actor.use_runner?).to be false }
32
+ end
33
+
34
+ describe '#check_subtask?' do
35
+ it { expect(actor.check_subtask?).to be false }
36
+ end
37
+
38
+ describe '#generate_task?' do
39
+ it { expect(actor.generate_task?).to be false }
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/attention/signal_detection/actors/update'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Attention::SignalDetection::Actor::Update do
6
+ subject(:actor) { described_class.new }
7
+
8
+ describe '#runner_class' do
9
+ it 'points to the SignalDetection runner' do
10
+ expect(actor.runner_class).to eq Legion::Extensions::Agentic::Attention::SignalDetection::Runners::SignalDetection
11
+ end
12
+ end
13
+
14
+ describe '#runner_function' do
15
+ it 'is update_signal_detection' do
16
+ expect(actor.runner_function).to eq 'update_signal_detection'
17
+ end
18
+ end
19
+
20
+ describe '#time' do
21
+ it 'runs every 60 seconds' do
22
+ expect(actor.time).to eq 60
23
+ end
24
+ end
25
+
26
+ describe '#run_now?' do
27
+ it { expect(actor.run_now?).to be false }
28
+ end
29
+
30
+ describe '#use_runner?' do
31
+ it { expect(actor.use_runner?).to be false }
32
+ end
33
+
34
+ describe '#check_subtask?' do
35
+ it { expect(actor.check_subtask?).to be false }
36
+ end
37
+
38
+ describe '#generate_task?' do
39
+ it { expect(actor.generate_task?).to be false }
40
+ end
41
+ end
@@ -12,7 +12,7 @@ RSpec.describe Legion::Extensions::Agentic::Attention::Spotlight::Client do
12
12
  expect(client).to respond_to(:scan_targets)
13
13
  expect(client).to respond_to(:check_peripheral)
14
14
  expect(client).to respond_to(:check_capture)
15
- expect(client).to respond_to(:release_focus)
15
+ expect(client).to respond_to(:release_spotlight)
16
16
  expect(client).to respond_to(:spotlight_report)
17
17
  expect(client).to respond_to(:most_salient)
18
18
  expect(client).to respond_to(:spotlight_state)
@@ -163,19 +163,19 @@ RSpec.describe Legion::Extensions::Agentic::Attention::Spotlight::Runners::Atten
163
163
  end
164
164
  end
165
165
 
166
- describe '#release_focus' do
166
+ describe '#release_spotlight' do
167
167
  it 'returns success true' do
168
- expect(client.release_focus[:success]).to be true
168
+ expect(client.release_spotlight[:success]).to be true
169
169
  end
170
170
 
171
171
  it 'returns released true' do
172
- expect(client.release_focus[:released]).to be true
172
+ expect(client.release_spotlight[:released]).to be true
173
173
  end
174
174
 
175
175
  it 'sets mode back to idle' do
176
176
  id = reg[:target_id]
177
177
  client.focus_spotlight(target_id: id)
178
- client.release_focus
178
+ client.release_spotlight
179
179
  expect(client.spotlight_state[:spotlight][:mode]).to eq(:idle)
180
180
  end
181
181
  end
data/spec/spec_helper.rb CHANGED
@@ -35,6 +35,10 @@ module Legion
35
35
  end
36
36
  end
37
37
 
38
+ # Prevent actor files that `require 'legion/extensions/actors/every'` from re-loading
39
+ # the real gem file; the stub class above is already defined and sufficient for specs.
40
+ $LOADED_FEATURES << 'legion/extensions/actors/every' unless $LOADED_FEATURES.include?('legion/extensions/actors/every')
41
+
38
42
  require 'legion/extensions/agentic/attention'
39
43
 
40
44
  RSpec.configure do |config|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-agentic-attention
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -163,6 +163,7 @@ files:
163
163
  - lex-agentic-attention.gemspec
164
164
  - lib/legion/extensions/agentic/attention.rb
165
165
  - lib/legion/extensions/agentic/attention/arousal.rb
166
+ - lib/legion/extensions/agentic/attention/arousal/actors/update.rb
166
167
  - lib/legion/extensions/agentic/attention/arousal/client.rb
167
168
  - lib/legion/extensions/agentic/attention/arousal/helpers/arousal_model.rb
168
169
  - lib/legion/extensions/agentic/attention/arousal/helpers/constants.rb
@@ -177,6 +178,7 @@ files:
177
178
  - lib/legion/extensions/agentic/attention/blindspot/runners/cognitive_blindspot.rb
178
179
  - lib/legion/extensions/agentic/attention/blindspot/version.rb
179
180
  - lib/legion/extensions/agentic/attention/blink.rb
181
+ - lib/legion/extensions/agentic/attention/blink/actors/decay.rb
180
182
  - lib/legion/extensions/agentic/attention/blink/client.rb
181
183
  - lib/legion/extensions/agentic/attention/blink/helpers/blink_engine.rb
182
184
  - lib/legion/extensions/agentic/attention/blink/helpers/constants.rb
@@ -254,6 +256,7 @@ files:
254
256
  - lib/legion/extensions/agentic/attention/prism/runners/cognitive_prism.rb
255
257
  - lib/legion/extensions/agentic/attention/prism/version.rb
256
258
  - lib/legion/extensions/agentic/attention/regulation.rb
259
+ - lib/legion/extensions/agentic/attention/regulation/actors/update.rb
257
260
  - lib/legion/extensions/agentic/attention/regulation/client.rb
258
261
  - lib/legion/extensions/agentic/attention/regulation/helpers/attention_controller.rb
259
262
  - lib/legion/extensions/agentic/attention/regulation/helpers/attention_target.rb
@@ -268,6 +271,7 @@ files:
268
271
  - lib/legion/extensions/agentic/attention/relevance_theory/runners/relevance_theory.rb
269
272
  - lib/legion/extensions/agentic/attention/relevance_theory/version.rb
270
273
  - lib/legion/extensions/agentic/attention/salience.rb
274
+ - lib/legion/extensions/agentic/attention/salience/actors/compute.rb
271
275
  - lib/legion/extensions/agentic/attention/salience/client.rb
272
276
  - lib/legion/extensions/agentic/attention/salience/helpers/constants.rb
273
277
  - lib/legion/extensions/agentic/attention/salience/helpers/salience_map.rb
@@ -290,6 +294,7 @@ files:
290
294
  - lib/legion/extensions/agentic/attention/sensory_gating/runners/sensory_gating.rb
291
295
  - lib/legion/extensions/agentic/attention/sensory_gating/version.rb
292
296
  - lib/legion/extensions/agentic/attention/signal_detection.rb
297
+ - lib/legion/extensions/agentic/attention/signal_detection/actors/update.rb
293
298
  - lib/legion/extensions/agentic/attention/signal_detection/client.rb
294
299
  - lib/legion/extensions/agentic/attention/signal_detection/helpers/constants.rb
295
300
  - lib/legion/extensions/agentic/attention/signal_detection/helpers/detection_engine.rb
@@ -345,6 +350,7 @@ files:
345
350
  - lib/legion/extensions/agentic/attention/telescope/runners/cognitive_telescope.rb
346
351
  - lib/legion/extensions/agentic/attention/telescope/version.rb
347
352
  - lib/legion/extensions/agentic/attention/version.rb
353
+ - spec/legion/extensions/agentic/attention/arousal/actors/update_spec.rb
348
354
  - spec/legion/extensions/agentic/attention/arousal/client_spec.rb
349
355
  - spec/legion/extensions/agentic/attention/arousal/helpers/arousal_model_spec.rb
350
356
  - spec/legion/extensions/agentic/attention/arousal/helpers/constants_spec.rb
@@ -355,6 +361,7 @@ files:
355
361
  - spec/legion/extensions/agentic/attention/blindspot/helpers/constants_spec.rb
356
362
  - spec/legion/extensions/agentic/attention/blindspot/helpers/knowledge_boundary_spec.rb
357
363
  - spec/legion/extensions/agentic/attention/blindspot/runners/cognitive_blindspot_spec.rb
364
+ - spec/legion/extensions/agentic/attention/blink/actors/decay_spec.rb
358
365
  - spec/legion/extensions/agentic/attention/blink/client_spec.rb
359
366
  - spec/legion/extensions/agentic/attention/blink/helpers/blink_engine_spec.rb
360
367
  - spec/legion/extensions/agentic/attention/blink/helpers/constants_spec.rb
@@ -409,6 +416,7 @@ files:
409
416
  - spec/legion/extensions/agentic/attention/prism/helpers/prism_engine_spec.rb
410
417
  - spec/legion/extensions/agentic/attention/prism/helpers/spectral_component_spec.rb
411
418
  - spec/legion/extensions/agentic/attention/prism/runners/cognitive_prism_spec.rb
419
+ - spec/legion/extensions/agentic/attention/regulation/actors/update_spec.rb
412
420
  - spec/legion/extensions/agentic/attention/regulation/client_spec.rb
413
421
  - spec/legion/extensions/agentic/attention/regulation/helpers/attention_controller_spec.rb
414
422
  - spec/legion/extensions/agentic/attention/regulation/helpers/attention_target_spec.rb
@@ -417,6 +425,7 @@ files:
417
425
  - spec/legion/extensions/agentic/attention/relevance_theory/helpers/relevance_engine_spec.rb
418
426
  - spec/legion/extensions/agentic/attention/relevance_theory/relevance_theory_spec.rb
419
427
  - spec/legion/extensions/agentic/attention/relevance_theory/runners/relevance_theory_spec.rb
428
+ - spec/legion/extensions/agentic/attention/salience/actors/compute_spec.rb
420
429
  - spec/legion/extensions/agentic/attention/salience/client_spec.rb
421
430
  - spec/legion/extensions/agentic/attention/salience/helpers/constants_spec.rb
422
431
  - spec/legion/extensions/agentic/attention/salience/helpers/salience_map_spec.rb
@@ -429,6 +438,7 @@ files:
429
438
  - spec/legion/extensions/agentic/attention/sensory_gating/helpers/gating_engine_spec.rb
430
439
  - spec/legion/extensions/agentic/attention/sensory_gating/helpers/sensory_filter_spec.rb
431
440
  - spec/legion/extensions/agentic/attention/sensory_gating/runners/sensory_gating_spec.rb
441
+ - spec/legion/extensions/agentic/attention/signal_detection/actors/update_spec.rb
432
442
  - spec/legion/extensions/agentic/attention/signal_detection/client_spec.rb
433
443
  - spec/legion/extensions/agentic/attention/signal_detection/helpers/constants_spec.rb
434
444
  - spec/legion/extensions/agentic/attention/signal_detection/helpers/detection_engine_spec.rb