lex-agentic-affect 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.
Files changed (218) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +12 -0
  3. data/Gemfile +5 -0
  4. data/LICENSE +21 -0
  5. data/README.md +13 -0
  6. data/lex-agentic-affect.gemspec +30 -0
  7. data/lib/legion/extensions/agentic/affect/appraisal/client.rb +20 -0
  8. data/lib/legion/extensions/agentic/affect/appraisal/helpers/appraisal.rb +112 -0
  9. data/lib/legion/extensions/agentic/affect/appraisal/helpers/appraisal_engine.rb +129 -0
  10. data/lib/legion/extensions/agentic/affect/appraisal/helpers/constants.rb +43 -0
  11. data/lib/legion/extensions/agentic/affect/appraisal/runners/appraisal.rb +105 -0
  12. data/lib/legion/extensions/agentic/affect/appraisal/version.rb +13 -0
  13. data/lib/legion/extensions/agentic/affect/appraisal.rb +19 -0
  14. data/lib/legion/extensions/agentic/affect/cognitive_empathy/client.rb +19 -0
  15. data/lib/legion/extensions/agentic/affect/cognitive_empathy/helpers/constants.rb +37 -0
  16. data/lib/legion/extensions/agentic/affect/cognitive_empathy/helpers/empathy_engine.rb +151 -0
  17. data/lib/legion/extensions/agentic/affect/cognitive_empathy/helpers/perspective.rb +92 -0
  18. data/lib/legion/extensions/agentic/affect/cognitive_empathy/runners/cognitive_empathy.rb +93 -0
  19. data/lib/legion/extensions/agentic/affect/cognitive_empathy/version.rb +13 -0
  20. data/lib/legion/extensions/agentic/affect/cognitive_empathy.rb +20 -0
  21. data/lib/legion/extensions/agentic/affect/contagion/client.rb +28 -0
  22. data/lib/legion/extensions/agentic/affect/contagion/helpers/constants.rb +34 -0
  23. data/lib/legion/extensions/agentic/affect/contagion/helpers/contagion_engine.rb +184 -0
  24. data/lib/legion/extensions/agentic/affect/contagion/helpers/meme.rb +97 -0
  25. data/lib/legion/extensions/agentic/affect/contagion/runners/cognitive_contagion.rb +125 -0
  26. data/lib/legion/extensions/agentic/affect/contagion/version.rb +13 -0
  27. data/lib/legion/extensions/agentic/affect/contagion.rb +19 -0
  28. data/lib/legion/extensions/agentic/affect/defusion/client.rb +28 -0
  29. data/lib/legion/extensions/agentic/affect/defusion/helpers/constants.rb +64 -0
  30. data/lib/legion/extensions/agentic/affect/defusion/helpers/defusion_engine.rb +167 -0
  31. data/lib/legion/extensions/agentic/affect/defusion/helpers/thought.rb +92 -0
  32. data/lib/legion/extensions/agentic/affect/defusion/runners/cognitive_defusion.rb +127 -0
  33. data/lib/legion/extensions/agentic/affect/defusion/version.rb +13 -0
  34. data/lib/legion/extensions/agentic/affect/defusion.rb +19 -0
  35. data/lib/legion/extensions/agentic/affect/emotion/actors/momentum_decay.rb +45 -0
  36. data/lib/legion/extensions/agentic/affect/emotion/client.rb +36 -0
  37. data/lib/legion/extensions/agentic/affect/emotion/helpers/baseline.rb +52 -0
  38. data/lib/legion/extensions/agentic/affect/emotion/helpers/momentum.rb +52 -0
  39. data/lib/legion/extensions/agentic/affect/emotion/helpers/valence.rb +92 -0
  40. data/lib/legion/extensions/agentic/affect/emotion/runners/gut.rb +102 -0
  41. data/lib/legion/extensions/agentic/affect/emotion/runners/valence.rb +120 -0
  42. data/lib/legion/extensions/agentic/affect/emotion/version.rb +13 -0
  43. data/lib/legion/extensions/agentic/affect/emotion.rb +20 -0
  44. data/lib/legion/extensions/agentic/affect/empathy/client.rb +21 -0
  45. data/lib/legion/extensions/agentic/affect/empathy/helpers/constants.rb +54 -0
  46. data/lib/legion/extensions/agentic/affect/empathy/helpers/mental_model.rb +185 -0
  47. data/lib/legion/extensions/agentic/affect/empathy/helpers/model_store.rb +88 -0
  48. data/lib/legion/extensions/agentic/affect/empathy/runners/empathy.rb +173 -0
  49. data/lib/legion/extensions/agentic/affect/empathy/version.rb +13 -0
  50. data/lib/legion/extensions/agentic/affect/empathy.rb +20 -0
  51. data/lib/legion/extensions/agentic/affect/fatigue/client.rb +26 -0
  52. data/lib/legion/extensions/agentic/affect/fatigue/helpers/constants.rb +54 -0
  53. data/lib/legion/extensions/agentic/affect/fatigue/helpers/energy_model.rb +181 -0
  54. data/lib/legion/extensions/agentic/affect/fatigue/helpers/fatigue_store.rb +146 -0
  55. data/lib/legion/extensions/agentic/affect/fatigue/runners/fatigue.rb +89 -0
  56. data/lib/legion/extensions/agentic/affect/fatigue/version.rb +13 -0
  57. data/lib/legion/extensions/agentic/affect/fatigue.rb +19 -0
  58. data/lib/legion/extensions/agentic/affect/flow/client.rb +25 -0
  59. data/lib/legion/extensions/agentic/affect/flow/helpers/constants.rb +84 -0
  60. data/lib/legion/extensions/agentic/affect/flow/helpers/flow_detector.rb +166 -0
  61. data/lib/legion/extensions/agentic/affect/flow/runners/flow.rb +129 -0
  62. data/lib/legion/extensions/agentic/affect/flow/version.rb +13 -0
  63. data/lib/legion/extensions/agentic/affect/flow.rb +18 -0
  64. data/lib/legion/extensions/agentic/affect/interoception/actors/decay.rb +45 -0
  65. data/lib/legion/extensions/agentic/affect/interoception/client.rb +28 -0
  66. data/lib/legion/extensions/agentic/affect/interoception/helpers/body_budget.rb +152 -0
  67. data/lib/legion/extensions/agentic/affect/interoception/helpers/constants.rb +68 -0
  68. data/lib/legion/extensions/agentic/affect/interoception/helpers/somatic_marker.rb +75 -0
  69. data/lib/legion/extensions/agentic/affect/interoception/runners/interoception.rb +101 -0
  70. data/lib/legion/extensions/agentic/affect/interoception/version.rb +13 -0
  71. data/lib/legion/extensions/agentic/affect/interoception.rb +20 -0
  72. data/lib/legion/extensions/agentic/affect/mood/client.rb +21 -0
  73. data/lib/legion/extensions/agentic/affect/mood/helpers/constants.rb +78 -0
  74. data/lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb +154 -0
  75. data/lib/legion/extensions/agentic/affect/mood/runners/mood.rb +122 -0
  76. data/lib/legion/extensions/agentic/affect/mood/version.rb +13 -0
  77. data/lib/legion/extensions/agentic/affect/mood.rb +18 -0
  78. data/lib/legion/extensions/agentic/affect/motivation/client.rb +26 -0
  79. data/lib/legion/extensions/agentic/affect/motivation/helpers/constants.rb +48 -0
  80. data/lib/legion/extensions/agentic/affect/motivation/helpers/drive_state.rb +98 -0
  81. data/lib/legion/extensions/agentic/affect/motivation/helpers/motivation_store.rb +106 -0
  82. data/lib/legion/extensions/agentic/affect/motivation/runners/motivation.rb +165 -0
  83. data/lib/legion/extensions/agentic/affect/motivation/version.rb +13 -0
  84. data/lib/legion/extensions/agentic/affect/motivation.rb +19 -0
  85. data/lib/legion/extensions/agentic/affect/reappraisal/actors/auto_regulate.rb +45 -0
  86. data/lib/legion/extensions/agentic/affect/reappraisal/client.rb +28 -0
  87. data/lib/legion/extensions/agentic/affect/reappraisal/helpers/constants.rb +82 -0
  88. data/lib/legion/extensions/agentic/affect/reappraisal/helpers/emotional_event.rb +98 -0
  89. data/lib/legion/extensions/agentic/affect/reappraisal/helpers/llm_enhancer.rb +88 -0
  90. data/lib/legion/extensions/agentic/affect/reappraisal/helpers/reappraisal_engine.rb +153 -0
  91. data/lib/legion/extensions/agentic/affect/reappraisal/runners/cognitive_reappraisal.rb +164 -0
  92. data/lib/legion/extensions/agentic/affect/reappraisal/version.rb +13 -0
  93. data/lib/legion/extensions/agentic/affect/reappraisal.rb +20 -0
  94. data/lib/legion/extensions/agentic/affect/regulation/client.rb +25 -0
  95. data/lib/legion/extensions/agentic/affect/regulation/helpers/constants.rb +71 -0
  96. data/lib/legion/extensions/agentic/affect/regulation/helpers/regulation_model.rb +175 -0
  97. data/lib/legion/extensions/agentic/affect/regulation/runners/emotional_regulation.rb +127 -0
  98. data/lib/legion/extensions/agentic/affect/regulation/version.rb +13 -0
  99. data/lib/legion/extensions/agentic/affect/regulation.rb +18 -0
  100. data/lib/legion/extensions/agentic/affect/resilience/client.rb +27 -0
  101. data/lib/legion/extensions/agentic/affect/resilience/helpers/adversity_tracker.rb +130 -0
  102. data/lib/legion/extensions/agentic/affect/resilience/helpers/constants.rb +79 -0
  103. data/lib/legion/extensions/agentic/affect/resilience/helpers/resilience_model.rb +165 -0
  104. data/lib/legion/extensions/agentic/affect/resilience/runners/resilience.rb +150 -0
  105. data/lib/legion/extensions/agentic/affect/resilience/version.rb +13 -0
  106. data/lib/legion/extensions/agentic/affect/resilience.rb +19 -0
  107. data/lib/legion/extensions/agentic/affect/resonance/client.rb +24 -0
  108. data/lib/legion/extensions/agentic/affect/resonance/helpers/category.rb +75 -0
  109. data/lib/legion/extensions/agentic/affect/resonance/helpers/constants.rb +47 -0
  110. data/lib/legion/extensions/agentic/affect/resonance/helpers/resonance_engine.rb +115 -0
  111. data/lib/legion/extensions/agentic/affect/resonance/runners/cognitive_resonance.rb +94 -0
  112. data/lib/legion/extensions/agentic/affect/resonance/version.rb +13 -0
  113. data/lib/legion/extensions/agentic/affect/resonance.rb +19 -0
  114. data/lib/legion/extensions/agentic/affect/reward/client.rb +26 -0
  115. data/lib/legion/extensions/agentic/affect/reward/helpers/constants.rb +67 -0
  116. data/lib/legion/extensions/agentic/affect/reward/helpers/reward_signal.rb +178 -0
  117. data/lib/legion/extensions/agentic/affect/reward/helpers/reward_store.rb +142 -0
  118. data/lib/legion/extensions/agentic/affect/reward/runners/reward.rb +92 -0
  119. data/lib/legion/extensions/agentic/affect/reward/version.rb +13 -0
  120. data/lib/legion/extensions/agentic/affect/reward.rb +19 -0
  121. data/lib/legion/extensions/agentic/affect/somatic_marker/actors/decay.rb +45 -0
  122. data/lib/legion/extensions/agentic/affect/somatic_marker/client.rb +29 -0
  123. data/lib/legion/extensions/agentic/affect/somatic_marker/helpers/body_state.rb +69 -0
  124. data/lib/legion/extensions/agentic/affect/somatic_marker/helpers/constants.rb +43 -0
  125. data/lib/legion/extensions/agentic/affect/somatic_marker/helpers/marker_store.rb +160 -0
  126. data/lib/legion/extensions/agentic/affect/somatic_marker/helpers/somatic_marker.rb +74 -0
  127. data/lib/legion/extensions/agentic/affect/somatic_marker/runners/somatic_marker.rb +132 -0
  128. data/lib/legion/extensions/agentic/affect/somatic_marker/version.rb +13 -0
  129. data/lib/legion/extensions/agentic/affect/somatic_marker.rb +20 -0
  130. data/lib/legion/extensions/agentic/affect/version.rb +11 -0
  131. data/lib/legion/extensions/agentic/affect.rb +34 -0
  132. data/spec/legion/extensions/agentic/affect/appraisal/client_spec.rb +52 -0
  133. data/spec/legion/extensions/agentic/affect/appraisal/helpers/appraisal_engine_spec.rb +161 -0
  134. data/spec/legion/extensions/agentic/affect/appraisal/helpers/appraisal_spec.rb +175 -0
  135. data/spec/legion/extensions/agentic/affect/appraisal/helpers/constants_spec.rb +49 -0
  136. data/spec/legion/extensions/agentic/affect/appraisal/runners/appraisal_spec.rb +116 -0
  137. data/spec/legion/extensions/agentic/affect/cognitive_empathy/client_spec.rb +62 -0
  138. data/spec/legion/extensions/agentic/affect/cognitive_empathy/helpers/empathy_engine_spec.rb +316 -0
  139. data/spec/legion/extensions/agentic/affect/cognitive_empathy/helpers/perspective_spec.rb +132 -0
  140. data/spec/legion/extensions/agentic/affect/cognitive_empathy/runners/cognitive_empathy_spec.rb +200 -0
  141. data/spec/legion/extensions/agentic/affect/contagion/client_spec.rb +63 -0
  142. data/spec/legion/extensions/agentic/affect/contagion/helpers/constants_spec.rb +86 -0
  143. data/spec/legion/extensions/agentic/affect/contagion/helpers/contagion_engine_spec.rb +241 -0
  144. data/spec/legion/extensions/agentic/affect/contagion/helpers/meme_spec.rb +160 -0
  145. data/spec/legion/extensions/agentic/affect/contagion/runners/cognitive_contagion_spec.rb +211 -0
  146. data/spec/legion/extensions/agentic/affect/defusion/client_spec.rb +80 -0
  147. data/spec/legion/extensions/agentic/affect/defusion/helpers/constants_spec.rb +84 -0
  148. data/spec/legion/extensions/agentic/affect/defusion/helpers/defusion_engine_spec.rb +250 -0
  149. data/spec/legion/extensions/agentic/affect/defusion/helpers/thought_spec.rb +178 -0
  150. data/spec/legion/extensions/agentic/affect/defusion/runners/cognitive_defusion_spec.rb +185 -0
  151. data/spec/legion/extensions/agentic/affect/emotion/actors/momentum_decay_spec.rb +46 -0
  152. data/spec/legion/extensions/agentic/affect/emotion/client_spec.rb +46 -0
  153. data/spec/legion/extensions/agentic/affect/emotion/helpers/baseline_spec.rb +48 -0
  154. data/spec/legion/extensions/agentic/affect/emotion/helpers/momentum_spec.rb +45 -0
  155. data/spec/legion/extensions/agentic/affect/emotion/helpers/valence_spec.rb +91 -0
  156. data/spec/legion/extensions/agentic/affect/emotion/runners/gut_spec.rb +73 -0
  157. data/spec/legion/extensions/agentic/affect/emotion/runners/valence_spec.rb +67 -0
  158. data/spec/legion/extensions/agentic/affect/empathy/client_spec.rb +20 -0
  159. data/spec/legion/extensions/agentic/affect/empathy/helpers/constants_spec.rb +23 -0
  160. data/spec/legion/extensions/agentic/affect/empathy/helpers/mental_model_spec.rb +150 -0
  161. data/spec/legion/extensions/agentic/affect/empathy/helpers/model_store_spec.rb +94 -0
  162. data/spec/legion/extensions/agentic/affect/empathy/runners/empathy_spec.rb +127 -0
  163. data/spec/legion/extensions/agentic/affect/fatigue/client_spec.rb +66 -0
  164. data/spec/legion/extensions/agentic/affect/fatigue/helpers/constants_spec.rb +130 -0
  165. data/spec/legion/extensions/agentic/affect/fatigue/helpers/energy_model_spec.rb +281 -0
  166. data/spec/legion/extensions/agentic/affect/fatigue/helpers/fatigue_store_spec.rb +157 -0
  167. data/spec/legion/extensions/agentic/affect/fatigue/runners/fatigue_spec.rb +127 -0
  168. data/spec/legion/extensions/agentic/affect/flow/client_spec.rb +58 -0
  169. data/spec/legion/extensions/agentic/affect/flow/helpers/constants_spec.rb +112 -0
  170. data/spec/legion/extensions/agentic/affect/flow/helpers/flow_detector_spec.rb +268 -0
  171. data/spec/legion/extensions/agentic/affect/flow/runners/flow_spec.rb +222 -0
  172. data/spec/legion/extensions/agentic/affect/interoception/client_spec.rb +52 -0
  173. data/spec/legion/extensions/agentic/affect/interoception/helpers/body_budget_spec.rb +178 -0
  174. data/spec/legion/extensions/agentic/affect/interoception/helpers/somatic_marker_spec.rb +120 -0
  175. data/spec/legion/extensions/agentic/affect/interoception/runners/interoception_spec.rb +108 -0
  176. data/spec/legion/extensions/agentic/affect/mood/client_spec.rb +20 -0
  177. data/spec/legion/extensions/agentic/affect/mood/helpers/constants_spec.rb +29 -0
  178. data/spec/legion/extensions/agentic/affect/mood/helpers/mood_state_spec.rb +94 -0
  179. data/spec/legion/extensions/agentic/affect/mood/runners/mood_spec.rb +71 -0
  180. data/spec/legion/extensions/agentic/affect/motivation/client_spec.rb +35 -0
  181. data/spec/legion/extensions/agentic/affect/motivation/helpers/constants_spec.rb +111 -0
  182. data/spec/legion/extensions/agentic/affect/motivation/helpers/drive_state_spec.rb +183 -0
  183. data/spec/legion/extensions/agentic/affect/motivation/helpers/motivation_store_spec.rb +185 -0
  184. data/spec/legion/extensions/agentic/affect/motivation/runners/motivation_spec.rb +248 -0
  185. data/spec/legion/extensions/agentic/affect/reappraisal/actors/auto_regulate_spec.rb +46 -0
  186. data/spec/legion/extensions/agentic/affect/reappraisal/client_spec.rb +64 -0
  187. data/spec/legion/extensions/agentic/affect/reappraisal/helpers/constants_spec.rb +102 -0
  188. data/spec/legion/extensions/agentic/affect/reappraisal/helpers/emotional_event_spec.rb +177 -0
  189. data/spec/legion/extensions/agentic/affect/reappraisal/helpers/llm_enhancer_spec.rb +161 -0
  190. data/spec/legion/extensions/agentic/affect/reappraisal/helpers/reappraisal_engine_spec.rb +211 -0
  191. data/spec/legion/extensions/agentic/affect/reappraisal/runners/cognitive_reappraisal_spec.rb +312 -0
  192. data/spec/legion/extensions/agentic/affect/regulation/client_spec.rb +61 -0
  193. data/spec/legion/extensions/agentic/affect/regulation/helpers/constants_spec.rb +108 -0
  194. data/spec/legion/extensions/agentic/affect/regulation/helpers/regulation_model_spec.rb +200 -0
  195. data/spec/legion/extensions/agentic/affect/regulation/runners/emotional_regulation_spec.rb +190 -0
  196. data/spec/legion/extensions/agentic/affect/resilience/client_spec.rb +36 -0
  197. data/spec/legion/extensions/agentic/affect/resilience/helpers/adversity_tracker_spec.rb +164 -0
  198. data/spec/legion/extensions/agentic/affect/resilience/helpers/constants_spec.rb +78 -0
  199. data/spec/legion/extensions/agentic/affect/resilience/helpers/resilience_model_spec.rb +133 -0
  200. data/spec/legion/extensions/agentic/affect/resilience/runners/resilience_spec.rb +150 -0
  201. data/spec/legion/extensions/agentic/affect/resonance/client_spec.rb +66 -0
  202. data/spec/legion/extensions/agentic/affect/resonance/cognitive_resonance_spec.rb +27 -0
  203. data/spec/legion/extensions/agentic/affect/resonance/helpers/category_spec.rb +146 -0
  204. data/spec/legion/extensions/agentic/affect/resonance/helpers/constants_spec.rb +104 -0
  205. data/spec/legion/extensions/agentic/affect/resonance/helpers/resonance_engine_spec.rb +189 -0
  206. data/spec/legion/extensions/agentic/affect/resonance/runners/cognitive_resonance_spec.rb +197 -0
  207. data/spec/legion/extensions/agentic/affect/reward/client_spec.rb +42 -0
  208. data/spec/legion/extensions/agentic/affect/reward/helpers/constants_spec.rb +91 -0
  209. data/spec/legion/extensions/agentic/affect/reward/helpers/reward_signal_spec.rb +296 -0
  210. data/spec/legion/extensions/agentic/affect/reward/helpers/reward_store_spec.rb +167 -0
  211. data/spec/legion/extensions/agentic/affect/reward/runners/reward_spec.rb +149 -0
  212. data/spec/legion/extensions/agentic/affect/somatic_marker/client_spec.rb +83 -0
  213. data/spec/legion/extensions/agentic/affect/somatic_marker/helpers/body_state_spec.rb +155 -0
  214. data/spec/legion/extensions/agentic/affect/somatic_marker/helpers/marker_store_spec.rb +233 -0
  215. data/spec/legion/extensions/agentic/affect/somatic_marker/helpers/somatic_marker_spec.rb +172 -0
  216. data/spec/legion/extensions/agentic/affect/somatic_marker/runners/somatic_marker_spec.rb +181 -0
  217. data/spec/spec_helper.rb +46 -0
  218. metadata +302 -0
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/affect/emotion/version'
4
+ require 'legion/extensions/agentic/affect/emotion/helpers/valence'
5
+ require 'legion/extensions/agentic/affect/emotion/helpers/baseline'
6
+ require 'legion/extensions/agentic/affect/emotion/helpers/momentum'
7
+ require 'legion/extensions/agentic/affect/emotion/runners/valence'
8
+ require 'legion/extensions/agentic/affect/emotion/runners/gut'
9
+ require 'legion/extensions/agentic/affect/emotion/client'
10
+
11
+ module Legion
12
+ module Extensions
13
+ module Agentic
14
+ module Affect
15
+ module Emotion
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Empathy
8
+ class Client
9
+ include Runners::Empathy
10
+
11
+ attr_reader :model_store
12
+
13
+ def initialize(model_store: nil, **)
14
+ @model_store = model_store || Helpers::ModelStore.new
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Empathy
8
+ module Helpers
9
+ module Constants
10
+ # Mental state dimensions tracked per agent
11
+ MENTAL_STATE_DIMENSIONS = %i[
12
+ believed_goal
13
+ emotional_state
14
+ attention_focus
15
+ confidence_level
16
+ cooperation_stance
17
+ ].freeze
18
+
19
+ # How quickly mental models update (EMA alpha)
20
+ MODEL_UPDATE_ALPHA = 0.2
21
+
22
+ # How long before a mental model is considered stale (seconds)
23
+ MODEL_STALENESS_THRESHOLD = 300
24
+
25
+ # Maximum number of tracked agents
26
+ MAX_TRACKED_AGENTS = 100
27
+
28
+ # Maximum interaction history per agent
29
+ MAX_INTERACTION_HISTORY = 50
30
+
31
+ # Prediction confidence thresholds
32
+ PREDICTION_CONFIDENT = 0.7
33
+ PREDICTION_UNCERTAIN = 0.4
34
+
35
+ # Cooperation stance values
36
+ COOPERATION_STANCES = %i[cooperative neutral competitive unknown].freeze
37
+
38
+ # Emotional state labels for other agents
39
+ INFERRED_EMOTIONS = %i[
40
+ calm focused stressed frustrated curious cautious enthusiastic unknown
41
+ ].freeze
42
+
43
+ # Perspective-taking accuracy tracking window
44
+ ACCURACY_WINDOW = 20
45
+
46
+ # Mental model decay rate (per decay cycle)
47
+ MODEL_DECAY_RATE = 0.01
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,185 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Empathy
8
+ module Helpers
9
+ class MentalModel
10
+ attr_reader :agent_id, :believed_goal, :emotional_state, :attention_focus,
11
+ :confidence_level, :cooperation_stance, :interaction_history,
12
+ :predictions, :created_at, :updated_at
13
+
14
+ def initialize(agent_id:)
15
+ @agent_id = agent_id
16
+ @believed_goal = nil
17
+ @emotional_state = :unknown
18
+ @attention_focus = nil
19
+ @confidence_level = 0.5
20
+ @cooperation_stance = :unknown
21
+ @interaction_history = []
22
+ @predictions = []
23
+ @prediction_outcomes = []
24
+ @created_at = Time.now.utc
25
+ @updated_at = @created_at
26
+ end
27
+
28
+ def update_from_observation(observation)
29
+ @updated_at = Time.now.utc
30
+
31
+ update_believed_goal(observation[:goal]) if observation[:goal]
32
+ update_emotional_state(observation[:emotion]) if observation[:emotion]
33
+ update_attention(observation[:attention]) if observation[:attention]
34
+ update_cooperation(observation[:cooperation]) if observation[:cooperation]
35
+ update_confidence(observation)
36
+
37
+ record_interaction(observation)
38
+ end
39
+
40
+ def predict_reaction(scenario)
41
+ prediction = {
42
+ prediction_id: SecureRandom.uuid,
43
+ scenario: scenario,
44
+ predicted_at: Time.now.utc,
45
+ likely_response: infer_response(scenario),
46
+ emotional_shift: infer_emotional_shift(scenario),
47
+ cooperation_shift: infer_cooperation_shift(scenario),
48
+ confidence: prediction_confidence
49
+ }
50
+
51
+ @predictions << prediction
52
+ @predictions = @predictions.last(Constants::MAX_INTERACTION_HISTORY)
53
+ prediction
54
+ end
55
+
56
+ def record_prediction_outcome(prediction_id:, actual_response:, accurate:)
57
+ pred = @predictions.find { |p| p[:prediction_id] == prediction_id }
58
+ return nil unless pred
59
+
60
+ pred[:actual_response] = actual_response
61
+ pred[:accurate] = accurate
62
+
63
+ @prediction_outcomes << { prediction_id: prediction_id, accurate: accurate, at: Time.now.utc }
64
+ @prediction_outcomes = @prediction_outcomes.last(Constants::ACCURACY_WINDOW)
65
+ accurate
66
+ end
67
+
68
+ def prediction_accuracy
69
+ return nil if @prediction_outcomes.empty?
70
+
71
+ correct = @prediction_outcomes.count { |o| o[:accurate] }
72
+ correct.to_f / @prediction_outcomes.size
73
+ end
74
+
75
+ def stale?
76
+ (Time.now.utc - @updated_at) > Constants::MODEL_STALENESS_THRESHOLD
77
+ end
78
+
79
+ def decay
80
+ @confidence_level = [(@confidence_level - Constants::MODEL_DECAY_RATE), 0.1].max
81
+ end
82
+
83
+ def to_h
84
+ {
85
+ agent_id: @agent_id,
86
+ believed_goal: @believed_goal,
87
+ emotional_state: @emotional_state,
88
+ attention_focus: @attention_focus,
89
+ confidence_level: @confidence_level,
90
+ cooperation_stance: @cooperation_stance,
91
+ interactions: @interaction_history.size,
92
+ predictions_made: @predictions.size,
93
+ prediction_accuracy: prediction_accuracy,
94
+ stale: stale?,
95
+ created_at: @created_at,
96
+ updated_at: @updated_at
97
+ }
98
+ end
99
+
100
+ private
101
+
102
+ def update_believed_goal(goal)
103
+ @believed_goal = goal
104
+ end
105
+
106
+ def update_emotional_state(emotion)
107
+ sym = emotion.to_sym
108
+ @emotional_state = Constants::INFERRED_EMOTIONS.include?(sym) ? sym : :unknown
109
+ end
110
+
111
+ def update_attention(attention)
112
+ @attention_focus = attention
113
+ end
114
+
115
+ def update_cooperation(cooperation)
116
+ sym = cooperation.to_sym
117
+ @cooperation_stance = Constants::COOPERATION_STANCES.include?(sym) ? sym : :unknown
118
+ end
119
+
120
+ def update_confidence(observation)
121
+ evidence_strength = observation[:evidence_strength] || 0.5
122
+ alpha = Constants::MODEL_UPDATE_ALPHA
123
+ @confidence_level = (@confidence_level * (1 - alpha)) + (evidence_strength * alpha)
124
+ @confidence_level = @confidence_level.clamp(0.0, 1.0)
125
+ end
126
+
127
+ def record_interaction(observation)
128
+ @interaction_history << {
129
+ type: observation[:interaction_type] || :observation,
130
+ summary: observation[:summary],
131
+ at: Time.now.utc
132
+ }
133
+ @interaction_history = @interaction_history.last(Constants::MAX_INTERACTION_HISTORY)
134
+ end
135
+
136
+ def infer_response(scenario)
137
+ case @cooperation_stance
138
+ when :cooperative
139
+ scenario[:cooperative_option] || :likely_agree
140
+ when :competitive
141
+ scenario[:competitive_option] || :likely_resist
142
+ when :neutral
143
+ @confidence_level > 0.5 ? :likely_consider : :unpredictable
144
+ else
145
+ :unpredictable
146
+ end
147
+ end
148
+
149
+ def infer_emotional_shift(scenario)
150
+ impact = scenario[:emotional_impact] || :neutral
151
+ case impact
152
+ when :positive
153
+ :likely_positive
154
+ when :negative
155
+ stressed_states = %i[stressed frustrated cautious]
156
+ stressed_states.include?(@emotional_state) ? :likely_escalate : :likely_negative
157
+ else
158
+ :likely_stable
159
+ end
160
+ end
161
+
162
+ def infer_cooperation_shift(scenario)
163
+ return :stable if scenario[:impact_on_agent].nil?
164
+
165
+ case scenario[:impact_on_agent]
166
+ when :beneficial then :likely_more_cooperative
167
+ when :harmful then :likely_less_cooperative
168
+ else :stable
169
+ end
170
+ end
171
+
172
+ def prediction_confidence
173
+ base = @confidence_level
174
+ staleness_penalty = stale? ? 0.2 : 0.0
175
+ history_bonus = [@interaction_history.size / 20.0, 0.2].min
176
+
177
+ (base - staleness_penalty + history_bonus).clamp(0.1, 0.9)
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Empathy
8
+ module Helpers
9
+ class ModelStore
10
+ attr_reader :models
11
+
12
+ def initialize
13
+ @models = {}
14
+ end
15
+
16
+ def get(agent_id)
17
+ @models[agent_id.to_s]
18
+ end
19
+
20
+ def get_or_create(agent_id)
21
+ key = agent_id.to_s
22
+ @models[key] ||= MentalModel.new(agent_id: key)
23
+ end
24
+
25
+ def update(agent_id, observation)
26
+ model = get_or_create(agent_id)
27
+ model.update_from_observation(observation)
28
+ evict_if_needed
29
+ model
30
+ end
31
+
32
+ def predict(agent_id, scenario)
33
+ model = get(agent_id)
34
+ return nil unless model
35
+
36
+ model.predict_reaction(scenario)
37
+ end
38
+
39
+ def decay_all
40
+ count = 0
41
+ @models.each_value do |model|
42
+ model.decay
43
+ count += 1
44
+ end
45
+ count
46
+ end
47
+
48
+ def remove_stale
49
+ stale_keys = @models.select { |_, m| m.stale? && m.interaction_history.empty? }.keys
50
+ stale_keys.each { |k| @models.delete(k) }
51
+ stale_keys.size
52
+ end
53
+
54
+ def all_models
55
+ @models.values
56
+ end
57
+
58
+ def by_cooperation(stance)
59
+ @models.values.select { |m| m.cooperation_stance == stance }
60
+ end
61
+
62
+ def by_emotion(emotion)
63
+ @models.values.select { |m| m.emotional_state == emotion }
64
+ end
65
+
66
+ def size
67
+ @models.size
68
+ end
69
+
70
+ def clear
71
+ @models.clear
72
+ end
73
+
74
+ private
75
+
76
+ def evict_if_needed
77
+ return unless @models.size > Constants::MAX_TRACKED_AGENTS
78
+
79
+ oldest = @models.min_by { |_, m| m.updated_at }
80
+ @models.delete(oldest[0]) if oldest
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Empathy
8
+ module Runners
9
+ module Empathy
10
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
11
+ Legion::Extensions::Helpers.const_defined?(:Lex)
12
+
13
+ def observe_agent(agent_id:, observation: {}, **)
14
+ model = model_store.update(agent_id, observation)
15
+ Legion::Logging.debug "[empathy] observed: agent=#{agent_id} emotion=#{model.emotional_state} " \
16
+ "cooperation=#{model.cooperation_stance}"
17
+
18
+ {
19
+ agent_id: agent_id,
20
+ emotional_state: model.emotional_state,
21
+ cooperation_stance: model.cooperation_stance,
22
+ believed_goal: model.believed_goal,
23
+ confidence: model.confidence_level
24
+ }
25
+ end
26
+
27
+ def predict_reaction(agent_id:, scenario: {}, **)
28
+ prediction = model_store.predict(agent_id, scenario)
29
+
30
+ if prediction
31
+ Legion::Logging.debug "[empathy] prediction: agent=#{agent_id} response=#{prediction[:likely_response]} " \
32
+ "confidence=#{prediction[:confidence].round(2)}"
33
+ prediction
34
+ else
35
+ Legion::Logging.debug "[empathy] no model for agent=#{agent_id}"
36
+ { error: :no_model, agent_id: agent_id }
37
+ end
38
+ end
39
+
40
+ def record_outcome(agent_id:, prediction_id:, actual_response:, accurate:, **)
41
+ model = model_store.get(agent_id)
42
+ return { error: :no_model } unless model
43
+
44
+ result = model.record_prediction_outcome(
45
+ prediction_id: prediction_id,
46
+ actual_response: actual_response,
47
+ accurate: accurate
48
+ )
49
+
50
+ if result.nil?
51
+ { error: :prediction_not_found }
52
+ else
53
+ Legion::Logging.info "[empathy] outcome recorded: agent=#{agent_id} accurate=#{accurate} " \
54
+ "accuracy=#{model.prediction_accuracy&.round(2)}"
55
+ { agent_id: agent_id, accurate: accurate, current_accuracy: model.prediction_accuracy }
56
+ end
57
+ end
58
+
59
+ def perspective_take(agent_id:, **)
60
+ model = model_store.get(agent_id)
61
+ return { error: :no_model, agent_id: agent_id } unless model
62
+
63
+ narrative = build_perspective_narrative(model)
64
+ Legion::Logging.debug "[empathy] perspective: agent=#{agent_id}"
65
+
66
+ {
67
+ agent_id: agent_id,
68
+ narrative: narrative,
69
+ model: model.to_h
70
+ }
71
+ end
72
+
73
+ def social_landscape(**)
74
+ models = model_store.all_models
75
+ cooperative = model_store.by_cooperation(:cooperative).size
76
+ competitive = model_store.by_cooperation(:competitive).size
77
+ stressed = model_store.by_emotion(:stressed).size + model_store.by_emotion(:frustrated).size
78
+
79
+ Legion::Logging.debug "[empathy] landscape: agents=#{models.size} cooperative=#{cooperative} " \
80
+ "competitive=#{competitive} stressed=#{stressed}"
81
+
82
+ {
83
+ tracked_agents: models.size,
84
+ cooperative_count: cooperative,
85
+ competitive_count: competitive,
86
+ stressed_count: stressed,
87
+ stances: stance_distribution(models),
88
+ emotions: emotion_distribution(models),
89
+ overall_climate: assess_climate(cooperative, competitive, stressed, models.size)
90
+ }
91
+ end
92
+
93
+ def decay_models(**)
94
+ decayed = model_store.decay_all
95
+ removed = model_store.remove_stale
96
+ Legion::Logging.debug "[empathy] decay: updated=#{decayed} stale_removed=#{removed}"
97
+ { decayed: decayed, stale_removed: removed }
98
+ end
99
+
100
+ def empathy_stats(**)
101
+ models = model_store.all_models
102
+ accuracies = models.filter_map(&:prediction_accuracy)
103
+
104
+ {
105
+ tracked_agents: model_store.size,
106
+ total_predictions: models.sum { |m| m.predictions.size },
107
+ avg_accuracy: accuracies.empty? ? nil : (accuracies.sum / accuracies.size).round(3),
108
+ stale_models: models.count(&:stale?),
109
+ cooperation_stances: stance_distribution(models)
110
+ }
111
+ end
112
+
113
+ private
114
+
115
+ def model_store
116
+ @model_store ||= Helpers::ModelStore.new
117
+ end
118
+
119
+ def build_perspective_narrative(model)
120
+ parts = []
121
+ parts << "Agent #{model.agent_id}"
122
+
123
+ parts << if model.believed_goal
124
+ "appears to be pursuing #{model.believed_goal}"
125
+ else
126
+ 'has no clearly observed goal'
127
+ end
128
+
129
+ parts << "and seems #{model.emotional_state}" unless model.emotional_state == :unknown
130
+ parts << "with a #{model.cooperation_stance} stance" unless model.cooperation_stance == :unknown
131
+
132
+ parts << "(model is stale — last updated #{((Time.now.utc - model.updated_at) / 60).round(1)} minutes ago)" if model.stale?
133
+
134
+ parts << "— prediction accuracy: #{(model.prediction_accuracy * 100).round(1)}%" if model.prediction_accuracy
135
+
136
+ parts.join(' ')
137
+ end
138
+
139
+ def stance_distribution(models)
140
+ dist = Hash.new(0)
141
+ models.each { |m| dist[m.cooperation_stance] += 1 }
142
+ dist
143
+ end
144
+
145
+ def emotion_distribution(models)
146
+ dist = Hash.new(0)
147
+ models.each { |m| dist[m.emotional_state] += 1 }
148
+ dist
149
+ end
150
+
151
+ def assess_climate(cooperative, competitive, stressed, total)
152
+ return :empty if total.zero?
153
+
154
+ coop_ratio = cooperative.to_f / total
155
+ stress_ratio = stressed.to_f / total
156
+
157
+ if coop_ratio > 0.6
158
+ :harmonious
159
+ elsif stress_ratio > 0.4
160
+ :tense
161
+ elsif competitive > cooperative
162
+ :adversarial
163
+ else
164
+ :neutral
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Empathy
8
+ VERSION = '0.1.0'
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'legion/extensions/agentic/affect/empathy/version'
5
+ require 'legion/extensions/agentic/affect/empathy/helpers/constants'
6
+ require 'legion/extensions/agentic/affect/empathy/helpers/mental_model'
7
+ require 'legion/extensions/agentic/affect/empathy/helpers/model_store'
8
+ require 'legion/extensions/agentic/affect/empathy/runners/empathy'
9
+ require 'legion/extensions/agentic/affect/empathy/client'
10
+
11
+ module Legion
12
+ module Extensions
13
+ module Agentic
14
+ module Affect
15
+ module Empathy
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/affect/fatigue/helpers/constants'
4
+ require 'legion/extensions/agentic/affect/fatigue/helpers/energy_model'
5
+ require 'legion/extensions/agentic/affect/fatigue/helpers/fatigue_store'
6
+ require 'legion/extensions/agentic/affect/fatigue/runners/fatigue'
7
+
8
+ module Legion
9
+ module Extensions
10
+ module Agentic
11
+ module Affect
12
+ module Fatigue
13
+ class Client
14
+ include Runners::Fatigue
15
+
16
+ attr_reader :fatigue_store
17
+
18
+ def initialize(fatigue_store: nil, **)
19
+ @fatigue_store = fatigue_store || Helpers::FatigueStore.new
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Fatigue
8
+ module Helpers
9
+ module Constants
10
+ MAX_ENERGY = 1.0
11
+ MIN_ENERGY = 0.0
12
+
13
+ RESTING_RECOVERY_RATE = 0.02
14
+ ACTIVE_DRAIN_RATE = 0.01
15
+ COGNITIVE_DRAIN_MULTIPLIER = 1.5
16
+ EMOTIONAL_DRAIN_MULTIPLIER = 1.3
17
+
18
+ FATIGUE_LEVELS = {
19
+ fresh: 0.8,
20
+ alert: 0.6,
21
+ tired: 0.4,
22
+ exhausted: 0.2,
23
+ depleted: 0.0
24
+ }.freeze
25
+
26
+ PERFORMANCE_DEGRADATION = {
27
+ fresh: 1.0,
28
+ alert: 0.95,
29
+ tired: 0.8,
30
+ exhausted: 0.6,
31
+ depleted: 0.3
32
+ }.freeze
33
+
34
+ RECOVERY_MODES = %i[active_rest light_duty full_rest sleep].freeze
35
+
36
+ RECOVERY_RATES = {
37
+ active_rest: 0.005,
38
+ light_duty: 0.01,
39
+ full_rest: 0.02,
40
+ sleep: 0.05
41
+ }.freeze
42
+
43
+ REST_THRESHOLD = 0.3
44
+ CRITICAL_THRESHOLD = 0.15
45
+ SECOND_WIND_CHANCE = 0.05
46
+ BURNOUT_THRESHOLD = 50
47
+ MAX_HISTORY = 100
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end