lex-joint-attention 0.1.0

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.
@@ -0,0 +1,228 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::JointAttention::Runners::JointAttention do
4
+ let(:client) { Legion::Extensions::JointAttention::Client.new }
5
+
6
+ let!(:target_result) do
7
+ client.create_attention_target(name: 'shared-task', domain: :work, priority: 0.7, creator: 'agent-1')
8
+ end
9
+ let(:target_id) { target_result[:target][:id] }
10
+
11
+ describe '#create_attention_target' do
12
+ it 'returns success: true' do
13
+ expect(target_result[:success]).to be true
14
+ end
15
+
16
+ it 'returns target with all fields' do
17
+ t = target_result[:target]
18
+ expect(t).to include(:id, :name, :domain, :priority, :creator_agent_id, :created_at, :attendee_count)
19
+ end
20
+
21
+ it 'sets the name correctly' do
22
+ expect(target_result[:target][:name]).to eq('shared-task')
23
+ end
24
+
25
+ it 'uses default priority when not specified' do
26
+ result = client.create_attention_target(name: 'default-prio', domain: :x, creator: 'a')
27
+ const = Legion::Extensions::JointAttention::Helpers::Constants::DEFAULT_FOCUS
28
+ expect(result[:target][:priority]).to be_within(0.01).of(const)
29
+ end
30
+
31
+ it 'auto-joins the creator' do
32
+ expect(target_result[:target][:attendee_count]).to eq(1)
33
+ end
34
+ end
35
+
36
+ describe '#join_attention' do
37
+ it 'joins a second agent and returns success' do
38
+ result = client.join_attention(target_id: target_id, agent_id: 'agent-2')
39
+ expect(result[:success]).to be true
40
+ expect(result[:result]).to eq(:joined)
41
+ end
42
+
43
+ it 'returns success for already_attending' do
44
+ client.join_attention(target_id: target_id, agent_id: 'agent-2')
45
+ result = client.join_attention(target_id: target_id, agent_id: 'agent-2')
46
+ expect(result[:success]).to be true
47
+ expect(result[:result]).to eq(:already_attending)
48
+ end
49
+
50
+ it 'returns success: false for unknown target' do
51
+ result = client.join_attention(target_id: 'bogus', agent_id: 'agent-2')
52
+ expect(result[:success]).to be false
53
+ expect(result[:result]).to eq(:target_not_found)
54
+ end
55
+
56
+ it 'includes target_id and agent_id in response' do
57
+ result = client.join_attention(target_id: target_id, agent_id: 'agent-2')
58
+ expect(result[:target_id]).to eq(target_id)
59
+ expect(result[:agent_id]).to eq('agent-2')
60
+ end
61
+
62
+ it 'accepts a gaze keyword' do
63
+ result = client.join_attention(target_id: target_id, agent_id: 'agent-2', gaze: :deadline)
64
+ expect(result[:success]).to be true
65
+ end
66
+ end
67
+
68
+ describe '#leave_attention' do
69
+ before { client.join_attention(target_id: target_id, agent_id: 'agent-2') }
70
+
71
+ it 'removes the agent and returns success' do
72
+ result = client.leave_attention(target_id: target_id, agent_id: 'agent-2')
73
+ expect(result[:success]).to be true
74
+ expect(result[:result]).to eq(:removed)
75
+ end
76
+
77
+ it 'returns success: false for not_found' do
78
+ result = client.leave_attention(target_id: target_id, agent_id: 'never-joined')
79
+ expect(result[:success]).to be false
80
+ end
81
+
82
+ it 'returns success: false for unknown target' do
83
+ result = client.leave_attention(target_id: 'bogus', agent_id: 'agent-2')
84
+ expect(result[:success]).to be false
85
+ end
86
+ end
87
+
88
+ describe '#direct_attention' do
89
+ it 'directs to_agent to target and returns success' do
90
+ result = client.direct_attention(from_agent: 'agent-1', to_agent: 'agent-2', target_id: target_id)
91
+ expect(result[:success]).to be true
92
+ expect(result[:result]).to eq(:directed)
93
+ end
94
+
95
+ it 'returns success: false for unknown target' do
96
+ result = client.direct_attention(from_agent: 'agent-1', to_agent: 'agent-2', target_id: 'bogus')
97
+ expect(result[:success]).to be false
98
+ end
99
+
100
+ it 'returns success: false when referrer is not attending' do
101
+ result = client.direct_attention(from_agent: 'nobody', to_agent: 'agent-2', target_id: target_id)
102
+ expect(result[:success]).to be false
103
+ expect(result[:result]).to eq(:referrer_not_attending)
104
+ end
105
+
106
+ it 'includes from_agent and to_agent in response' do
107
+ result = client.direct_attention(from_agent: 'agent-1', to_agent: 'agent-2', target_id: target_id)
108
+ expect(result[:from_agent]).to eq('agent-1')
109
+ expect(result[:to_agent]).to eq('agent-2')
110
+ end
111
+ end
112
+
113
+ describe '#establish_mutual_awareness' do
114
+ before { client.join_attention(target_id: target_id, agent_id: 'agent-2') }
115
+
116
+ it 'establishes shared awareness and returns success' do
117
+ result = client.establish_mutual_awareness(target_id: target_id, agent_a: 'agent-1', agent_b: 'agent-2')
118
+ expect(result[:success]).to be true
119
+ expect(result[:result]).to eq(:established)
120
+ end
121
+
122
+ it 'returns success: false for unknown target' do
123
+ result = client.establish_mutual_awareness(target_id: 'bogus', agent_a: 'agent-1', agent_b: 'agent-2')
124
+ expect(result[:success]).to be false
125
+ end
126
+
127
+ it 'returns success: false when one agent is not attending' do
128
+ result = client.establish_mutual_awareness(target_id: target_id, agent_a: 'agent-1', agent_b: 'stranger')
129
+ expect(result[:success]).to be false
130
+ end
131
+
132
+ it 'includes agent_a and agent_b in response' do
133
+ result = client.establish_mutual_awareness(target_id: target_id, agent_a: 'agent-1', agent_b: 'agent-2')
134
+ expect(result[:agent_a]).to eq('agent-1')
135
+ expect(result[:agent_b]).to eq('agent-2')
136
+ end
137
+ end
138
+
139
+ describe '#update_gaze' do
140
+ it 'updates gaze direction and returns success' do
141
+ result = client.update_gaze(target_id: target_id, agent_id: 'agent-1', gaze: :risk)
142
+ expect(result[:success]).to be true
143
+ expect(result[:result]).to eq(:updated)
144
+ expect(result[:gaze]).to eq(:risk)
145
+ end
146
+
147
+ it 'returns success: false for unknown target' do
148
+ result = client.update_gaze(target_id: 'bogus', agent_id: 'agent-1', gaze: :risk)
149
+ expect(result[:success]).to be false
150
+ end
151
+
152
+ it 'returns success: false for unknown agent' do
153
+ result = client.update_gaze(target_id: target_id, agent_id: 'nobody', gaze: :risk)
154
+ expect(result[:success]).to be false
155
+ end
156
+ end
157
+
158
+ describe '#shared_focus' do
159
+ it 'returns shared targets between two agents' do
160
+ client.join_attention(target_id: target_id, agent_id: 'agent-2')
161
+ result = client.shared_focus(agent_a: 'agent-1', agent_b: 'agent-2')
162
+ expect(result[:success]).to be true
163
+ expect(result[:count]).to eq(1)
164
+ expect(result[:shared_targets].first[:id]).to eq(target_id)
165
+ end
166
+
167
+ it 'returns empty when no shared targets' do
168
+ result = client.shared_focus(agent_a: 'agent-1', agent_b: 'loner')
169
+ expect(result[:success]).to be true
170
+ expect(result[:count]).to eq(0)
171
+ expect(result[:shared_targets]).to be_empty
172
+ end
173
+
174
+ it 'includes agent_a and agent_b in response' do
175
+ result = client.shared_focus(agent_a: 'agent-1', agent_b: 'agent-2')
176
+ expect(result[:agent_a]).to eq('agent-1')
177
+ expect(result[:agent_b]).to eq('agent-2')
178
+ end
179
+ end
180
+
181
+ describe '#attention_targets_for' do
182
+ it 'returns targets for an agent' do
183
+ result = client.attention_targets_for(agent_id: 'agent-1')
184
+ expect(result[:success]).to be true
185
+ expect(result[:count]).to eq(1)
186
+ expect(result[:targets].first[:id]).to eq(target_id)
187
+ end
188
+
189
+ it 'returns empty for unknown agent' do
190
+ result = client.attention_targets_for(agent_id: 'nobody')
191
+ expect(result[:success]).to be true
192
+ expect(result[:count]).to eq(0)
193
+ end
194
+
195
+ it 'includes agent_id in response' do
196
+ result = client.attention_targets_for(agent_id: 'agent-1')
197
+ expect(result[:agent_id]).to eq('agent-1')
198
+ end
199
+ end
200
+
201
+ describe '#update_joint_attention' do
202
+ it 'returns success with stats' do
203
+ result = client.update_joint_attention
204
+ expect(result[:success]).to be true
205
+ expect(result).to have_key(:targets)
206
+ expect(result).to have_key(:agents)
207
+ expect(result).to have_key(:history)
208
+ end
209
+
210
+ it 'reflects current target count' do
211
+ result = client.update_joint_attention
212
+ expect(result[:targets]).to be >= 1
213
+ end
214
+ end
215
+
216
+ describe '#joint_attention_stats' do
217
+ it 'returns success with full stats hash' do
218
+ result = client.joint_attention_stats
219
+ expect(result[:success]).to be true
220
+ expect(result[:stats]).to include(:target_count, :agent_count, :history_size, :targets)
221
+ end
222
+
223
+ it 'stats reflect created targets' do
224
+ stats = client.joint_attention_stats
225
+ expect(stats[:stats][:target_count]).to eq(1)
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+
5
+ module Legion
6
+ module Logging
7
+ def self.debug(_msg); end
8
+ def self.info(_msg); end
9
+ def self.warn(_msg); end
10
+ def self.error(_msg); end
11
+ end
12
+ end
13
+
14
+ require 'legion/extensions/joint_attention'
15
+
16
+ RSpec.configure do |config|
17
+ config.example_status_persistence_file_path = '.rspec_status'
18
+ config.disable_monkey_patching!
19
+ config.expect_with(:rspec) { |c| c.syntax = :expect }
20
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lex-joint-attention
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Esity
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: legion-gaia
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ description: Tomasello's joint attention framework for LegionIO — shared focus between
27
+ agents, mutual awareness tracking, referential communication, and collaborative
28
+ attention management with focus decay and working-memory constraints.
29
+ email:
30
+ - matthewdiverson@gmail.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - Gemfile
36
+ - lex-joint-attention.gemspec
37
+ - lib/legion/extensions/joint_attention.rb
38
+ - lib/legion/extensions/joint_attention/actors/decay.rb
39
+ - lib/legion/extensions/joint_attention/client.rb
40
+ - lib/legion/extensions/joint_attention/helpers/attention_target.rb
41
+ - lib/legion/extensions/joint_attention/helpers/constants.rb
42
+ - lib/legion/extensions/joint_attention/helpers/joint_focus_manager.rb
43
+ - lib/legion/extensions/joint_attention/runners/joint_attention.rb
44
+ - lib/legion/extensions/joint_attention/version.rb
45
+ - spec/legion/extensions/joint_attention/client_spec.rb
46
+ - spec/legion/extensions/joint_attention/helpers/attention_target_spec.rb
47
+ - spec/legion/extensions/joint_attention/helpers/joint_focus_manager_spec.rb
48
+ - spec/legion/extensions/joint_attention/runners/joint_attention_spec.rb
49
+ - spec/spec_helper.rb
50
+ homepage: https://github.com/LegionIO/lex-joint-attention
51
+ licenses:
52
+ - MIT
53
+ metadata:
54
+ homepage_uri: https://github.com/LegionIO/lex-joint-attention
55
+ source_code_uri: https://github.com/LegionIO/lex-joint-attention
56
+ documentation_uri: https://github.com/LegionIO/lex-joint-attention
57
+ changelog_uri: https://github.com/LegionIO/lex-joint-attention
58
+ bug_tracker_uri: https://github.com/LegionIO/lex-joint-attention/issues
59
+ rubygems_mfa_required: 'true'
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '3.4'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubygems_version: 3.6.9
75
+ specification_version: 4
76
+ summary: LEX Joint Attention
77
+ test_files: []