lex-agentic-affect 0.1.6 → 0.1.7
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 +12 -0
- data/lib/legion/extensions/agentic/affect/cognitive_empathy/runners/cognitive_empathy.rb +22 -0
- data/lib/legion/extensions/agentic/affect/emotion/helpers/valence.rb +1 -0
- data/lib/legion/extensions/agentic/affect/empathy/helpers/mental_model.rb +11 -3
- data/lib/legion/extensions/agentic/affect/empathy/helpers/model_store.rb +61 -0
- data/lib/legion/extensions/agentic/affect/empathy/runners/empathy.rb +14 -0
- data/lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb +40 -0
- data/lib/legion/extensions/agentic/affect/personality_state.rb +73 -0
- data/lib/legion/extensions/agentic/affect/version.rb +1 -1
- data/lib/legion/extensions/agentic/affect.rb +1 -0
- data/spec/legion/extensions/agentic/affect/cognitive_empathy/runners/cognitive_empathy_human_obs_spec.rb +63 -0
- data/spec/legion/extensions/agentic/affect/emotion/helpers/valence_direct_address_spec.rb +9 -0
- data/spec/legion/extensions/agentic/affect/empathy/helpers/model_store_apollo_spec.rb +153 -0
- data/spec/legion/extensions/agentic/affect/empathy/runners/empathy_human_obs_spec.rb +65 -0
- data/spec/legion/extensions/agentic/affect/mood/helpers/mood_state_apollo_spec.rb +81 -0
- data/spec/legion/extensions/agentic/affect/personality_state_spec.rb +108 -0
- metadata +8 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 74630b3aaa2366764ebb2af5fe332a6ce6b51778f2c6bcc22669348e051bf9f7
|
|
4
|
+
data.tar.gz: a241ea19f718a589b0bb0a3ef609d7dbf16bfad7ada03cb2a29288910779a26c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 560024c502ab7b4b43995b330dd7b17f8f7b086c8afbcbd9c48798d895a25ada189bb3e9deb34ce606be7206cb4a6264df98940c05b811bda0c560dc11317f12
|
|
7
|
+
data.tar.gz: fd7148b82824f8d2079bbdf8508bd9ff1031d11eb68ee00d78ca2eb52f6848dc401c0d444d976b8761224f2f7f90bb9ed8e9f6e46a85dbdd5c2056e0882b14b4
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.7] - 2026-03-31
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Empathy: `MentalModel` tracks `bond_role` and `channel`; partners start with 0.8 confidence
|
|
7
|
+
- Empathy: `ModelStore#update_from_human_observation` processes GAIA partner observation hashes
|
|
8
|
+
- Empathy: `ModelStore` Apollo Local persistence — `dirty?`, `mark_clean!`, `to_apollo_entries`, `from_apollo`
|
|
9
|
+
- Empathy runner: `observe_human_observations(human_observations:)` processes GAIA-passed obs arrays
|
|
10
|
+
- CognitiveEmpathy runner: `process_human_observations(human_observations:)` — perspective tracking + contagion (partner virulence 0.3, unknown 0.05)
|
|
11
|
+
- Valence: `:direct_address` source urgency 0.8 added to `SOURCE_URGENCY`
|
|
12
|
+
- MoodState: Apollo Local persistence — `dirty?`, `mark_clean!`, `to_apollo_entries`, `from_apollo`
|
|
13
|
+
- `PersonalityState`: new class modeling Big Five OCEAN traits with Apollo Local persistence
|
|
14
|
+
|
|
3
15
|
## [0.1.6] - 2026-03-30
|
|
4
16
|
|
|
5
17
|
### Changed
|
|
@@ -10,6 +10,28 @@ module Legion
|
|
|
10
10
|
include Helpers::Constants
|
|
11
11
|
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
12
12
|
|
|
13
|
+
def process_human_observations(human_observations: [], **)
|
|
14
|
+
return { processed: 0 } if human_observations.empty?
|
|
15
|
+
|
|
16
|
+
human_observations.each do |obs|
|
|
17
|
+
identity = obs[:identity].to_s
|
|
18
|
+
bond_role = obs[:bond_role] || :unknown
|
|
19
|
+
|
|
20
|
+
take_empathic_perspective(
|
|
21
|
+
agent_id: identity,
|
|
22
|
+
perspective_type: :affective,
|
|
23
|
+
predicted_state: { bond_role: bond_role, channel: obs[:channel] },
|
|
24
|
+
confidence: bond_role == :partner ? 0.7 : 0.4
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
virulence = bond_role == :partner ? 0.3 : 0.05
|
|
28
|
+
engine.emotional_contagion(emotion_valence: 0.5, intensity: virulence)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
log.debug("[cognitive_empathy] process_human_observations: count=#{human_observations.size}")
|
|
32
|
+
{ processed: human_observations.size }
|
|
33
|
+
end
|
|
34
|
+
|
|
13
35
|
def take_empathic_perspective(agent_id:, perspective_type:, predicted_state:, confidence: 0.5, **)
|
|
14
36
|
perspective = engine.take_perspective(
|
|
15
37
|
agent_id: agent_id,
|
|
@@ -9,14 +9,16 @@ module Legion
|
|
|
9
9
|
class MentalModel
|
|
10
10
|
attr_reader :agent_id, :believed_goal, :emotional_state, :attention_focus,
|
|
11
11
|
:confidence_level, :cooperation_stance, :interaction_history,
|
|
12
|
-
:predictions, :created_at, :updated_at
|
|
12
|
+
:predictions, :created_at, :updated_at, :bond_role, :channel
|
|
13
13
|
|
|
14
|
-
def initialize(agent_id:)
|
|
14
|
+
def initialize(agent_id:, bond_role: :unknown, channel: nil, confidence: nil)
|
|
15
15
|
@agent_id = agent_id
|
|
16
|
+
@bond_role = bond_role
|
|
17
|
+
@channel = channel
|
|
16
18
|
@believed_goal = nil
|
|
17
19
|
@emotional_state = :unknown
|
|
18
20
|
@attention_focus = nil
|
|
19
|
-
@confidence_level =
|
|
21
|
+
@confidence_level = confidence || partner_default_confidence(bond_role)
|
|
20
22
|
@cooperation_stance = :unknown
|
|
21
23
|
@interaction_history = []
|
|
22
24
|
@predictions = []
|
|
@@ -83,6 +85,8 @@ module Legion
|
|
|
83
85
|
def to_h
|
|
84
86
|
{
|
|
85
87
|
agent_id: @agent_id,
|
|
88
|
+
bond_role: @bond_role,
|
|
89
|
+
channel: @channel,
|
|
86
90
|
believed_goal: @believed_goal,
|
|
87
91
|
emotional_state: @emotional_state,
|
|
88
92
|
attention_focus: @attention_focus,
|
|
@@ -99,6 +103,10 @@ module Legion
|
|
|
99
103
|
|
|
100
104
|
private
|
|
101
105
|
|
|
106
|
+
def partner_default_confidence(bond_role)
|
|
107
|
+
bond_role == :partner ? 0.8 : 0.5
|
|
108
|
+
end
|
|
109
|
+
|
|
102
110
|
def update_believed_goal(goal)
|
|
103
111
|
@believed_goal = goal
|
|
104
112
|
end
|
|
@@ -11,6 +11,7 @@ module Legion
|
|
|
11
11
|
|
|
12
12
|
def initialize
|
|
13
13
|
@models = {}
|
|
14
|
+
@dirty = false
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def get(agent_id)
|
|
@@ -29,6 +30,66 @@ module Legion
|
|
|
29
30
|
model
|
|
30
31
|
end
|
|
31
32
|
|
|
33
|
+
def update_from_human_observation(observation)
|
|
34
|
+
identity = observation[:identity].to_s
|
|
35
|
+
bond_role = observation[:bond_role] || :unknown
|
|
36
|
+
channel = observation[:channel]
|
|
37
|
+
|
|
38
|
+
key = identity
|
|
39
|
+
model = @models[key] ||= MentalModel.new(agent_id: key, bond_role: bond_role, channel: channel)
|
|
40
|
+
evidence = bond_role == :partner ? 0.8 : 0.5
|
|
41
|
+
model.update_from_observation(
|
|
42
|
+
interaction_type: :human_observation,
|
|
43
|
+
evidence_strength: evidence,
|
|
44
|
+
summary: "channel=#{channel} content_type=#{observation[:content_type]} " \
|
|
45
|
+
"length=#{observation[:content_length]}"
|
|
46
|
+
)
|
|
47
|
+
evict_if_needed
|
|
48
|
+
@dirty = true
|
|
49
|
+
model
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def dirty?
|
|
53
|
+
@dirty
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def mark_clean!
|
|
57
|
+
@dirty = false
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def to_apollo_entries
|
|
61
|
+
@models.values.map do |model|
|
|
62
|
+
data = model.to_h.merge(
|
|
63
|
+
created_at: model.created_at.iso8601,
|
|
64
|
+
updated_at: model.updated_at.iso8601
|
|
65
|
+
)
|
|
66
|
+
{
|
|
67
|
+
content: ::JSON.generate(data.transform_keys(&:to_s)),
|
|
68
|
+
tags: ['empathy', 'mental_model', model.agent_id]
|
|
69
|
+
}
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def from_apollo(store:)
|
|
74
|
+
entries = store.query(tags: %w[empathy mental_model])
|
|
75
|
+
entries.each do |entry|
|
|
76
|
+
data = ::JSON.parse(entry[:content])
|
|
77
|
+
agent_id = data['agent_id']
|
|
78
|
+
next unless agent_id
|
|
79
|
+
|
|
80
|
+
bond_role = data['bond_role']&.to_sym || :unknown
|
|
81
|
+
channel = data['channel']&.to_sym
|
|
82
|
+
confidence = data['confidence_level']
|
|
83
|
+
|
|
84
|
+
model = MentalModel.new(agent_id: agent_id, bond_role: bond_role,
|
|
85
|
+
channel: channel, confidence: confidence)
|
|
86
|
+
@models[agent_id] = model
|
|
87
|
+
rescue ::JSON::ParserError => e
|
|
88
|
+
warn "[empathy] from_apollo: skipping invalid entry: #{e.message}"
|
|
89
|
+
next
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
32
93
|
def predict(agent_id, scenario)
|
|
33
94
|
model = get(agent_id)
|
|
34
95
|
return nil unless model
|
|
@@ -10,6 +10,20 @@ module Legion
|
|
|
10
10
|
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
11
11
|
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
12
12
|
|
|
13
|
+
def observe_human_observations(human_observations: [], **)
|
|
14
|
+
return { observed: 0, identities: [] } if human_observations.empty?
|
|
15
|
+
|
|
16
|
+
identities = human_observations.map do |obs|
|
|
17
|
+
model_store.update_from_human_observation(obs)
|
|
18
|
+
obs[:identity].to_s
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
log.debug("[empathy] human_observations: count=#{identities.size} " \
|
|
22
|
+
"identities=#{identities.join(',')}")
|
|
23
|
+
|
|
24
|
+
{ observed: identities.size, identities: identities }
|
|
25
|
+
end
|
|
26
|
+
|
|
13
27
|
def observe_agent(agent_id:, observation: {}, **)
|
|
14
28
|
model = model_store.update(agent_id, observation)
|
|
15
29
|
log.debug("[empathy] observed: agent=#{agent_id} emotion=#{model.emotional_state} " \
|
|
@@ -9,6 +9,8 @@ module Legion
|
|
|
9
9
|
class MoodState
|
|
10
10
|
attr_reader :current_mood, :valence, :arousal, :energy, :stability, :history, :tick_counter
|
|
11
11
|
|
|
12
|
+
DIRTY_THRESHOLD = 0.02
|
|
13
|
+
|
|
12
14
|
def initialize
|
|
13
15
|
@valence = 0.5
|
|
14
16
|
@arousal = 0.3
|
|
@@ -17,6 +19,8 @@ module Legion
|
|
|
17
19
|
@current_mood = :neutral
|
|
18
20
|
@history = []
|
|
19
21
|
@tick_counter = 0
|
|
22
|
+
@dirty = false
|
|
23
|
+
@last_persisted_valence = @valence
|
|
20
24
|
end
|
|
21
25
|
|
|
22
26
|
def update(inputs)
|
|
@@ -31,10 +35,42 @@ module Legion
|
|
|
31
35
|
compute_stability
|
|
32
36
|
classify_mood
|
|
33
37
|
record_history
|
|
38
|
+
check_dirty
|
|
34
39
|
|
|
35
40
|
@current_mood
|
|
36
41
|
end
|
|
37
42
|
|
|
43
|
+
def dirty?
|
|
44
|
+
@dirty
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def mark_clean!
|
|
48
|
+
@dirty = false
|
|
49
|
+
@last_persisted_valence = @valence
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def to_apollo_entries
|
|
53
|
+
[{
|
|
54
|
+
content: ::JSON.generate(to_h.transform_keys(&:to_s).except('modulations')),
|
|
55
|
+
tags: %w[affect state global]
|
|
56
|
+
}]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def from_apollo(store:)
|
|
60
|
+
entries = store.query(tags: %w[affect state global])
|
|
61
|
+
return if entries.empty?
|
|
62
|
+
|
|
63
|
+
data = ::JSON.parse(entries.first[:content])
|
|
64
|
+
@valence = data['valence'].to_f if data['valence']
|
|
65
|
+
@arousal = data['arousal'].to_f if data['arousal']
|
|
66
|
+
@energy = data['energy'].to_f if data['energy']
|
|
67
|
+
@current_mood = data['current_mood']&.to_sym || @current_mood
|
|
68
|
+
@last_persisted_valence = @valence
|
|
69
|
+
@dirty = false
|
|
70
|
+
rescue ::JSON::ParserError => e
|
|
71
|
+
warn "[mood_state] from_apollo: invalid entry: #{e.message}"
|
|
72
|
+
end
|
|
73
|
+
|
|
38
74
|
def modulations
|
|
39
75
|
Constants::MOOD_MODULATIONS.fetch(@current_mood, Constants::MOOD_MODULATIONS[:neutral])
|
|
40
76
|
end
|
|
@@ -90,6 +126,10 @@ module Legion
|
|
|
90
126
|
|
|
91
127
|
private
|
|
92
128
|
|
|
129
|
+
def check_dirty
|
|
130
|
+
@dirty = true if (@valence - @last_persisted_valence).abs >= DIRTY_THRESHOLD
|
|
131
|
+
end
|
|
132
|
+
|
|
93
133
|
def effective_alpha
|
|
94
134
|
base_alpha = Constants::MOOD_ALPHA
|
|
95
135
|
current_inertia = inertia
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agentic
|
|
6
|
+
module Affect
|
|
7
|
+
# PersonalityState models the Big Five (OCEAN) personality dimensions as global
|
|
8
|
+
# affect modifiers. Traits persist to Apollo Local tagged ['personality', 'ocean', 'global'].
|
|
9
|
+
class PersonalityState
|
|
10
|
+
TRAITS = %i[openness conscientiousness extraversion agreeableness neuroticism].freeze
|
|
11
|
+
DIRTY_THRESHOLD = 0.02
|
|
12
|
+
|
|
13
|
+
attr_reader(*TRAITS)
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
TRAITS.each { |t| instance_variable_set(:"@#{t}", 0.5) }
|
|
17
|
+
@dirty = false
|
|
18
|
+
@last_persisted = snapshot
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def update_trait(trait, value)
|
|
22
|
+
return unless TRAITS.include?(trait)
|
|
23
|
+
|
|
24
|
+
clamped = value.to_f.clamp(0.0, 1.0)
|
|
25
|
+
instance_variable_set(:"@#{trait}", clamped)
|
|
26
|
+
@dirty = true if (clamped - @last_persisted[trait]).abs >= DIRTY_THRESHOLD
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def dirty?
|
|
30
|
+
@dirty
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def mark_clean!
|
|
34
|
+
@dirty = false
|
|
35
|
+
@last_persisted = snapshot
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def to_apollo_entries
|
|
39
|
+
[{
|
|
40
|
+
content: ::JSON.generate(to_h.transform_keys(&:to_s)),
|
|
41
|
+
tags: %w[personality ocean global]
|
|
42
|
+
}]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def from_apollo(store:)
|
|
46
|
+
entries = store.query(tags: %w[personality ocean global])
|
|
47
|
+
return if entries.empty?
|
|
48
|
+
|
|
49
|
+
data = ::JSON.parse(entries.first[:content])
|
|
50
|
+
TRAITS.each do |trait|
|
|
51
|
+
val = data[trait.to_s]
|
|
52
|
+
instance_variable_set(:"@#{trait}", val.to_f.clamp(0.0, 1.0)) if val
|
|
53
|
+
end
|
|
54
|
+
@last_persisted = snapshot
|
|
55
|
+
@dirty = false
|
|
56
|
+
rescue ::JSON::ParserError => e
|
|
57
|
+
warn "[personality_state] from_apollo: invalid entry: #{e.message}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def to_h
|
|
61
|
+
TRAITS.to_h { |t| [t, instance_variable_get(:"@#{t}")] }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def snapshot
|
|
67
|
+
TRAITS.to_h { |t| [t, instance_variable_get(:"@#{t}")] }
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Affect::CognitiveEmpathy::Runners::CognitiveEmpathy do
|
|
4
|
+
let(:runner) do
|
|
5
|
+
obj = Object.new
|
|
6
|
+
obj.extend(described_class)
|
|
7
|
+
obj
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
let(:partner_obs) do
|
|
11
|
+
{
|
|
12
|
+
identity: 'esity',
|
|
13
|
+
bond_role: :partner,
|
|
14
|
+
channel: :cli,
|
|
15
|
+
content_type: :text,
|
|
16
|
+
content_length: 80,
|
|
17
|
+
direct_address: true,
|
|
18
|
+
timestamp: Time.now.utc
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
let(:unknown_obs) do
|
|
23
|
+
{
|
|
24
|
+
identity: 'stranger',
|
|
25
|
+
bond_role: :unknown,
|
|
26
|
+
channel: :teams,
|
|
27
|
+
content_type: :text,
|
|
28
|
+
content_length: 10,
|
|
29
|
+
direct_address: false,
|
|
30
|
+
timestamp: Time.now.utc
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe '#process_human_observations' do
|
|
35
|
+
it 'returns empty result for empty array' do
|
|
36
|
+
result = runner.process_human_observations(human_observations: [])
|
|
37
|
+
expect(result[:processed]).to eq(0)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'processes a partner observation and creates a perspective' do
|
|
41
|
+
result = runner.process_human_observations(human_observations: [partner_obs])
|
|
42
|
+
expect(result[:processed]).to eq(1)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'processes multiple observations' do
|
|
46
|
+
result = runner.process_human_observations(human_observations: [partner_obs, unknown_obs])
|
|
47
|
+
expect(result[:processed]).to eq(2)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'applies contagion with higher virulence for partner bond_role' do
|
|
51
|
+
before_level = runner.current_empathic_state[:contagion_level]
|
|
52
|
+
runner.process_human_observations(human_observations: [partner_obs])
|
|
53
|
+
after_level = runner.current_empathic_state[:contagion_level]
|
|
54
|
+
expect(after_level).to be >= before_level
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'applies contagion with lower virulence for unknown bond_role' do
|
|
58
|
+
runner.process_human_observations(human_observations: [unknown_obs])
|
|
59
|
+
result = runner.current_empathic_state
|
|
60
|
+
expect(result[:contagion_level]).to be >= 0.0
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Affect::Emotion::Helpers::Valence do
|
|
4
|
+
describe 'SOURCE_URGENCY' do
|
|
5
|
+
it 'includes :direct_address with urgency 0.8' do
|
|
6
|
+
expect(described_class::SOURCE_URGENCY[:direct_address]).to eq(0.8)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Affect::Empathy::Helpers::ModelStore do
|
|
4
|
+
subject(:store) { described_class.new }
|
|
5
|
+
|
|
6
|
+
let(:obs_partner) do
|
|
7
|
+
{
|
|
8
|
+
identity: 'esity',
|
|
9
|
+
bond_role: :partner,
|
|
10
|
+
channel: :cli,
|
|
11
|
+
content_type: :text,
|
|
12
|
+
content_length: 80,
|
|
13
|
+
direct_address: true,
|
|
14
|
+
timestamp: Time.now.utc
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
let(:obs_unknown) do
|
|
19
|
+
{
|
|
20
|
+
identity: 'stranger',
|
|
21
|
+
bond_role: :unknown,
|
|
22
|
+
channel: :teams,
|
|
23
|
+
content_type: :text,
|
|
24
|
+
content_length: 20,
|
|
25
|
+
direct_address: false,
|
|
26
|
+
timestamp: Time.now.utc
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe '#update_from_human_observation' do
|
|
31
|
+
it 'creates a model for the observed identity' do
|
|
32
|
+
model = store.update_from_human_observation(obs_partner)
|
|
33
|
+
expect(model.agent_id).to eq('esity')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'sets confidence to 0.8 for partner bond_role' do
|
|
37
|
+
model = store.update_from_human_observation(obs_partner)
|
|
38
|
+
expect(model.confidence_level).to be_within(0.05).of(0.8)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'sets confidence near 0.5 for unknown bond_role' do
|
|
42
|
+
model = store.update_from_human_observation(obs_unknown)
|
|
43
|
+
expect(model.confidence_level).to be_within(0.1).of(0.5)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'stores bond_role on the model' do
|
|
47
|
+
model = store.update_from_human_observation(obs_partner)
|
|
48
|
+
expect(model.bond_role).to eq(:partner)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'stores channel on the model' do
|
|
52
|
+
model = store.update_from_human_observation(obs_partner)
|
|
53
|
+
expect(model.channel).to eq(:cli)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'increments store size' do
|
|
57
|
+
store.update_from_human_observation(obs_partner)
|
|
58
|
+
store.update_from_human_observation(obs_unknown)
|
|
59
|
+
expect(store.size).to eq(2)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
describe '#dirty?' do
|
|
64
|
+
it 'is false on new store' do
|
|
65
|
+
expect(store.dirty?).to be false
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'is true after an update_from_human_observation' do
|
|
69
|
+
store.update_from_human_observation(obs_partner)
|
|
70
|
+
expect(store.dirty?).to be true
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'is false after mark_clean!' do
|
|
74
|
+
store.update_from_human_observation(obs_partner)
|
|
75
|
+
store.mark_clean!
|
|
76
|
+
expect(store.dirty?).to be false
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
describe '#mark_clean!' do
|
|
81
|
+
it 'resets dirty flag' do
|
|
82
|
+
store.update_from_human_observation(obs_partner)
|
|
83
|
+
store.mark_clean!
|
|
84
|
+
expect(store.dirty?).to be false
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
describe '#to_apollo_entries' do
|
|
89
|
+
it 'returns empty array when store is empty' do
|
|
90
|
+
expect(store.to_apollo_entries).to eq([])
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'returns one entry per model' do
|
|
94
|
+
store.update_from_human_observation(obs_partner)
|
|
95
|
+
store.update_from_human_observation(obs_unknown)
|
|
96
|
+
expect(store.to_apollo_entries.size).to eq(2)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it 'includes empathy, mental_model, and agent_id in tags' do
|
|
100
|
+
store.update_from_human_observation(obs_partner)
|
|
101
|
+
entry = store.to_apollo_entries.first
|
|
102
|
+
expect(entry[:tags]).to include('empathy', 'mental_model', 'esity')
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it 'serializes content as a JSON string' do
|
|
106
|
+
store.update_from_human_observation(obs_partner)
|
|
107
|
+
entry = store.to_apollo_entries.first
|
|
108
|
+
parsed = JSON.parse(entry[:content])
|
|
109
|
+
expect(parsed['agent_id']).to eq('esity')
|
|
110
|
+
expect(parsed).to have_key('confidence_level')
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
describe '#from_apollo' do
|
|
115
|
+
it 'restores models from apollo entries' do
|
|
116
|
+
store.update_from_human_observation(obs_partner)
|
|
117
|
+
entries = store.to_apollo_entries
|
|
118
|
+
|
|
119
|
+
new_store = described_class.new
|
|
120
|
+
apollo_stub = double('apollo_local')
|
|
121
|
+
allow(apollo_stub).to receive(:query).and_return(entries.map { |e| { content: e[:content] } })
|
|
122
|
+
|
|
123
|
+
new_store.from_apollo(store: apollo_stub)
|
|
124
|
+
expect(new_store.size).to eq(1)
|
|
125
|
+
expect(new_store.get('esity')).not_to be_nil
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it 'restores bond_role on loaded model' do
|
|
129
|
+
store.update_from_human_observation(obs_partner)
|
|
130
|
+
entries = store.to_apollo_entries
|
|
131
|
+
|
|
132
|
+
new_store = described_class.new
|
|
133
|
+
apollo_stub = double('apollo_local')
|
|
134
|
+
allow(apollo_stub).to receive(:query).and_return(entries.map { |e| { content: e[:content] } })
|
|
135
|
+
|
|
136
|
+
new_store.from_apollo(store: apollo_stub)
|
|
137
|
+
expect(new_store.get('esity').bond_role).to eq(:partner)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it 'handles empty apollo result gracefully' do
|
|
141
|
+
apollo_stub = double('apollo_local')
|
|
142
|
+
allow(apollo_stub).to receive(:query).and_return([])
|
|
143
|
+
expect { store.from_apollo(store: apollo_stub) }.not_to raise_error
|
|
144
|
+
expect(store.size).to eq(0)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
it 'skips entries with invalid JSON content' do
|
|
148
|
+
apollo_stub = double('apollo_local')
|
|
149
|
+
allow(apollo_stub).to receive(:query).and_return([{ content: 'not_json' }])
|
|
150
|
+
expect { store.from_apollo(store: apollo_stub) }.not_to raise_error
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Affect::Empathy::Runners::Empathy do
|
|
4
|
+
let(:client) { Legion::Extensions::Agentic::Affect::Empathy::Client.new }
|
|
5
|
+
|
|
6
|
+
let(:partner_obs) do
|
|
7
|
+
{
|
|
8
|
+
identity: 'esity',
|
|
9
|
+
bond_role: :partner,
|
|
10
|
+
channel: :cli,
|
|
11
|
+
content_type: :text,
|
|
12
|
+
content_length: 50,
|
|
13
|
+
direct_address: true,
|
|
14
|
+
timestamp: Time.now.utc
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
let(:known_obs) do
|
|
19
|
+
{
|
|
20
|
+
identity: 'alice',
|
|
21
|
+
bond_role: :known,
|
|
22
|
+
channel: :teams,
|
|
23
|
+
content_type: :text,
|
|
24
|
+
content_length: 20,
|
|
25
|
+
direct_address: false,
|
|
26
|
+
timestamp: Time.now.utc
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe '#observe_human_observations' do
|
|
31
|
+
it 'returns empty result for empty array' do
|
|
32
|
+
result = client.observe_human_observations(human_observations: [])
|
|
33
|
+
expect(result[:observed]).to eq(0)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'processes a single partner observation' do
|
|
37
|
+
result = client.observe_human_observations(human_observations: [partner_obs])
|
|
38
|
+
expect(result[:observed]).to eq(1)
|
|
39
|
+
expect(result[:identities]).to include('esity')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'processes multiple observations' do
|
|
43
|
+
result = client.observe_human_observations(human_observations: [partner_obs, known_obs])
|
|
44
|
+
expect(result[:observed]).to eq(2)
|
|
45
|
+
expect(result[:identities]).to include('esity', 'alice')
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'creates mental models for each identity' do
|
|
49
|
+
client.observe_human_observations(human_observations: [partner_obs])
|
|
50
|
+
model = client.model_store.get('esity')
|
|
51
|
+
expect(model).not_to be_nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'sets higher confidence for partner bond_role' do
|
|
55
|
+
client.observe_human_observations(human_observations: [partner_obs])
|
|
56
|
+
model = client.model_store.get('esity')
|
|
57
|
+
expect(model.confidence_level).to be > 0.7
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'marks store as dirty after observations' do
|
|
61
|
+
client.observe_human_observations(human_observations: [partner_obs])
|
|
62
|
+
expect(client.model_store.dirty?).to be true
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Affect::Mood::Helpers::MoodState do
|
|
4
|
+
subject(:state) { described_class.new }
|
|
5
|
+
|
|
6
|
+
describe '#dirty?' do
|
|
7
|
+
it 'is false on a fresh state' do
|
|
8
|
+
expect(state.dirty?).to be false
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'is true after an update that crosses the update interval' do
|
|
12
|
+
Legion::Extensions::Agentic::Affect::Mood::Helpers::Constants::UPDATE_INTERVAL.times do
|
|
13
|
+
state.update(valence: 0.8, arousal: 0.2, energy: 0.7)
|
|
14
|
+
end
|
|
15
|
+
expect(state.dirty?).to be true
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
describe '#mark_clean!' do
|
|
20
|
+
it 'resets dirty flag' do
|
|
21
|
+
Legion::Extensions::Agentic::Affect::Mood::Helpers::Constants::UPDATE_INTERVAL.times do
|
|
22
|
+
state.update(valence: 0.8, arousal: 0.2, energy: 0.7)
|
|
23
|
+
end
|
|
24
|
+
state.mark_clean!
|
|
25
|
+
expect(state.dirty?).to be false
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe '#to_apollo_entries' do
|
|
30
|
+
it 'returns an array with a single entry' do
|
|
31
|
+
entries = state.to_apollo_entries
|
|
32
|
+
expect(entries).to be_an(Array)
|
|
33
|
+
expect(entries.size).to eq(1)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'tags the entry with affect, state, and global' do
|
|
37
|
+
entry = state.to_apollo_entries.first
|
|
38
|
+
expect(entry[:tags]).to include('affect', 'state', 'global')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'serializes mood, valence, and arousal in content' do
|
|
42
|
+
entry = state.to_apollo_entries.first
|
|
43
|
+
parsed = JSON.parse(entry[:content])
|
|
44
|
+
expect(parsed).to have_key('current_mood')
|
|
45
|
+
expect(parsed).to have_key('valence')
|
|
46
|
+
expect(parsed).to have_key('arousal')
|
|
47
|
+
expect(parsed).to have_key('energy')
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
describe '#from_apollo' do
|
|
52
|
+
it 'restores valence, arousal, and energy from stored entry' do
|
|
53
|
+
# Drive state to a non-default value
|
|
54
|
+
20.times do
|
|
55
|
+
Legion::Extensions::Agentic::Affect::Mood::Helpers::Constants::UPDATE_INTERVAL.times do
|
|
56
|
+
state.update(valence: 0.9, arousal: 0.8, energy: 0.7)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
entries = state.to_apollo_entries
|
|
60
|
+
|
|
61
|
+
new_state = described_class.new
|
|
62
|
+
apollo_stub = double('apollo_local')
|
|
63
|
+
allow(apollo_stub).to receive(:query).and_return(entries.map { |e| { content: e[:content] } })
|
|
64
|
+
|
|
65
|
+
new_state.from_apollo(store: apollo_stub)
|
|
66
|
+
expect(new_state.valence).to be_within(0.1).of(state.valence)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'handles empty apollo result gracefully' do
|
|
70
|
+
apollo_stub = double('apollo_local')
|
|
71
|
+
allow(apollo_stub).to receive(:query).and_return([])
|
|
72
|
+
expect { state.from_apollo(store: apollo_stub) }.not_to raise_error
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'handles invalid JSON gracefully' do
|
|
76
|
+
apollo_stub = double('apollo_local')
|
|
77
|
+
allow(apollo_stub).to receive(:query).and_return([{ content: 'bad_json{' }])
|
|
78
|
+
expect { state.from_apollo(store: apollo_stub) }.not_to raise_error
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::Agentic::Affect::PersonalityState do
|
|
4
|
+
subject(:ps) { described_class.new }
|
|
5
|
+
|
|
6
|
+
describe '#initialize' do
|
|
7
|
+
it 'starts with neutral OCEAN traits at 0.5' do
|
|
8
|
+
expect(ps.openness).to eq(0.5)
|
|
9
|
+
expect(ps.conscientiousness).to eq(0.5)
|
|
10
|
+
expect(ps.extraversion).to eq(0.5)
|
|
11
|
+
expect(ps.agreeableness).to eq(0.5)
|
|
12
|
+
expect(ps.neuroticism).to eq(0.5)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'is not dirty initially' do
|
|
16
|
+
expect(ps.dirty?).to be false
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe '#update_trait' do
|
|
21
|
+
it 'updates a single trait' do
|
|
22
|
+
ps.update_trait(:openness, 0.8)
|
|
23
|
+
expect(ps.openness).to be_within(0.01).of(0.8)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'marks dirty after a significant change' do
|
|
27
|
+
ps.update_trait(:openness, 0.9)
|
|
28
|
+
expect(ps.dirty?).to be true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'does not mark dirty for a tiny change below threshold' do
|
|
32
|
+
ps.update_trait(:openness, 0.501)
|
|
33
|
+
expect(ps.dirty?).to be false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'clamps values to [0.0, 1.0]' do
|
|
37
|
+
ps.update_trait(:openness, 1.5)
|
|
38
|
+
expect(ps.openness).to eq(1.0)
|
|
39
|
+
ps.update_trait(:openness, -0.5)
|
|
40
|
+
expect(ps.openness).to eq(0.0)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe '#mark_clean!' do
|
|
45
|
+
it 'resets dirty flag' do
|
|
46
|
+
ps.update_trait(:openness, 0.9)
|
|
47
|
+
ps.mark_clean!
|
|
48
|
+
expect(ps.dirty?).to be false
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe '#to_apollo_entries' do
|
|
53
|
+
it 'returns one entry' do
|
|
54
|
+
entries = ps.to_apollo_entries
|
|
55
|
+
expect(entries.size).to eq(1)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'tags the entry with personality, ocean, and global' do
|
|
59
|
+
entry = ps.to_apollo_entries.first
|
|
60
|
+
expect(entry[:tags]).to include('personality', 'ocean', 'global')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'serializes all 5 OCEAN traits in content' do
|
|
64
|
+
entry = ps.to_apollo_entries.first
|
|
65
|
+
parsed = JSON.parse(entry[:content])
|
|
66
|
+
expect(parsed).to have_key('openness')
|
|
67
|
+
expect(parsed).to have_key('conscientiousness')
|
|
68
|
+
expect(parsed).to have_key('extraversion')
|
|
69
|
+
expect(parsed).to have_key('agreeableness')
|
|
70
|
+
expect(parsed).to have_key('neuroticism')
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
describe '#from_apollo' do
|
|
75
|
+
it 'restores OCEAN traits from stored entry' do
|
|
76
|
+
ps.update_trait(:openness, 0.9)
|
|
77
|
+
ps.update_trait(:neuroticism, 0.2)
|
|
78
|
+
entries = ps.to_apollo_entries
|
|
79
|
+
|
|
80
|
+
new_ps = described_class.new
|
|
81
|
+
apollo_stub = double('apollo_local')
|
|
82
|
+
allow(apollo_stub).to receive(:query).and_return(entries.map { |e| { content: e[:content] } })
|
|
83
|
+
|
|
84
|
+
new_ps.from_apollo(store: apollo_stub)
|
|
85
|
+
expect(new_ps.openness).to eq(0.9)
|
|
86
|
+
expect(new_ps.neuroticism).to eq(0.2)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'handles empty apollo result gracefully' do
|
|
90
|
+
apollo_stub = double('apollo_local')
|
|
91
|
+
allow(apollo_stub).to receive(:query).and_return([])
|
|
92
|
+
expect { ps.from_apollo(store: apollo_stub) }.not_to raise_error
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it 'handles invalid JSON gracefully' do
|
|
96
|
+
apollo_stub = double('apollo_local')
|
|
97
|
+
allow(apollo_stub).to receive(:query).and_return([{ content: 'bad{json' }])
|
|
98
|
+
expect { ps.from_apollo(store: apollo_stub) }.not_to raise_error
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
describe '#to_h' do
|
|
103
|
+
it 'returns hash of all 5 traits' do
|
|
104
|
+
h = ps.to_h
|
|
105
|
+
expect(h.keys).to include(:openness, :conscientiousness, :extraversion, :agreeableness, :neuroticism)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lex-agentic-affect
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -240,6 +240,7 @@ files:
|
|
|
240
240
|
- lib/legion/extensions/agentic/affect/motivation/helpers/motivation_store.rb
|
|
241
241
|
- lib/legion/extensions/agentic/affect/motivation/runners/motivation.rb
|
|
242
242
|
- lib/legion/extensions/agentic/affect/motivation/version.rb
|
|
243
|
+
- lib/legion/extensions/agentic/affect/personality_state.rb
|
|
243
244
|
- lib/legion/extensions/agentic/affect/reappraisal.rb
|
|
244
245
|
- lib/legion/extensions/agentic/affect/reappraisal/actors/auto_regulate.rb
|
|
245
246
|
- lib/legion/extensions/agentic/affect/reappraisal/client.rb
|
|
@@ -294,6 +295,7 @@ files:
|
|
|
294
295
|
- spec/legion/extensions/agentic/affect/cognitive_empathy/client_spec.rb
|
|
295
296
|
- spec/legion/extensions/agentic/affect/cognitive_empathy/helpers/empathy_engine_spec.rb
|
|
296
297
|
- spec/legion/extensions/agentic/affect/cognitive_empathy/helpers/perspective_spec.rb
|
|
298
|
+
- spec/legion/extensions/agentic/affect/cognitive_empathy/runners/cognitive_empathy_human_obs_spec.rb
|
|
297
299
|
- spec/legion/extensions/agentic/affect/cognitive_empathy/runners/cognitive_empathy_spec.rb
|
|
298
300
|
- spec/legion/extensions/agentic/affect/contagion/client_spec.rb
|
|
299
301
|
- spec/legion/extensions/agentic/affect/contagion/helpers/constants_spec.rb
|
|
@@ -309,13 +311,16 @@ files:
|
|
|
309
311
|
- spec/legion/extensions/agentic/affect/emotion/client_spec.rb
|
|
310
312
|
- spec/legion/extensions/agentic/affect/emotion/helpers/baseline_spec.rb
|
|
311
313
|
- spec/legion/extensions/agentic/affect/emotion/helpers/momentum_spec.rb
|
|
314
|
+
- spec/legion/extensions/agentic/affect/emotion/helpers/valence_direct_address_spec.rb
|
|
312
315
|
- spec/legion/extensions/agentic/affect/emotion/helpers/valence_spec.rb
|
|
313
316
|
- spec/legion/extensions/agentic/affect/emotion/runners/gut_spec.rb
|
|
314
317
|
- spec/legion/extensions/agentic/affect/emotion/runners/valence_spec.rb
|
|
315
318
|
- spec/legion/extensions/agentic/affect/empathy/client_spec.rb
|
|
316
319
|
- spec/legion/extensions/agentic/affect/empathy/helpers/constants_spec.rb
|
|
317
320
|
- spec/legion/extensions/agentic/affect/empathy/helpers/mental_model_spec.rb
|
|
321
|
+
- spec/legion/extensions/agentic/affect/empathy/helpers/model_store_apollo_spec.rb
|
|
318
322
|
- spec/legion/extensions/agentic/affect/empathy/helpers/model_store_spec.rb
|
|
323
|
+
- spec/legion/extensions/agentic/affect/empathy/runners/empathy_human_obs_spec.rb
|
|
319
324
|
- spec/legion/extensions/agentic/affect/empathy/runners/empathy_spec.rb
|
|
320
325
|
- spec/legion/extensions/agentic/affect/fatigue/client_spec.rb
|
|
321
326
|
- spec/legion/extensions/agentic/affect/fatigue/helpers/constants_spec.rb
|
|
@@ -332,6 +337,7 @@ files:
|
|
|
332
337
|
- spec/legion/extensions/agentic/affect/interoception/runners/interoception_spec.rb
|
|
333
338
|
- spec/legion/extensions/agentic/affect/mood/client_spec.rb
|
|
334
339
|
- spec/legion/extensions/agentic/affect/mood/helpers/constants_spec.rb
|
|
340
|
+
- spec/legion/extensions/agentic/affect/mood/helpers/mood_state_apollo_spec.rb
|
|
335
341
|
- spec/legion/extensions/agentic/affect/mood/helpers/mood_state_spec.rb
|
|
336
342
|
- spec/legion/extensions/agentic/affect/mood/runners/mood_spec.rb
|
|
337
343
|
- spec/legion/extensions/agentic/affect/motivation/client_spec.rb
|
|
@@ -339,6 +345,7 @@ files:
|
|
|
339
345
|
- spec/legion/extensions/agentic/affect/motivation/helpers/drive_state_spec.rb
|
|
340
346
|
- spec/legion/extensions/agentic/affect/motivation/helpers/motivation_store_spec.rb
|
|
341
347
|
- spec/legion/extensions/agentic/affect/motivation/runners/motivation_spec.rb
|
|
348
|
+
- spec/legion/extensions/agentic/affect/personality_state_spec.rb
|
|
342
349
|
- spec/legion/extensions/agentic/affect/reappraisal/actors/auto_regulate_spec.rb
|
|
343
350
|
- spec/legion/extensions/agentic/affect/reappraisal/client_spec.rb
|
|
344
351
|
- spec/legion/extensions/agentic/affect/reappraisal/helpers/constants_spec.rb
|