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,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Motivation
8
+ module Helpers
9
+ class DriveState
10
+ attr_reader :drives
11
+
12
+ def initialize
13
+ @drives = Constants::DRIVE_TYPES.to_h do |type|
14
+ [type, { level: 0.5, satisfied: false, last_signal: nil }]
15
+ end
16
+ end
17
+
18
+ def update_drive(type, signal)
19
+ return unless Constants::DRIVE_TYPES.include?(type)
20
+
21
+ clamped = signal.clamp(0.0, 1.0)
22
+ current = @drives[type][:level]
23
+ @drives[type][:level] = ema(current, clamped, Constants::DRIVE_ALPHA)
24
+ @drives[type][:satisfied] = @drives[type][:level] >= 0.6
25
+ @drives[type][:last_signal] = Time.now.utc
26
+ end
27
+
28
+ def drive_level(type)
29
+ return 0.0 unless Constants::DRIVE_TYPES.include?(type)
30
+
31
+ @drives[type][:level]
32
+ end
33
+
34
+ def satisfied?(type)
35
+ return false unless Constants::DRIVE_TYPES.include?(type)
36
+
37
+ @drives[type][:satisfied]
38
+ end
39
+
40
+ def intrinsic_average
41
+ levels = Constants::INTRINSIC_DRIVES.map { |d| @drives[d][:level] }
42
+ mean(levels)
43
+ end
44
+
45
+ def extrinsic_average
46
+ levels = Constants::EXTRINSIC_DRIVES.map { |d| @drives[d][:level] }
47
+ mean(levels)
48
+ end
49
+
50
+ def overall_level
51
+ all_levels = @drives.values.map { |d| d[:level] }
52
+ mean(all_levels)
53
+ end
54
+
55
+ def current_mode
56
+ level = overall_level
57
+ if level >= Constants::APPROACH_THRESHOLD
58
+ :approach
59
+ elsif level <= Constants::BURNOUT_THRESHOLD
60
+ :dormant
61
+ elsif level <= Constants::AVOIDANCE_THRESHOLD
62
+ :avoidance
63
+ else
64
+ :maintenance
65
+ end
66
+ end
67
+
68
+ def decay_all
69
+ @drives.each_key do |type|
70
+ current = @drives[type][:level]
71
+ decayed = [current - Constants::DRIVE_DECAY_RATE, 0.0].max
72
+ @drives[type][:level] = decayed
73
+ @drives[type][:satisfied] = decayed >= 0.6
74
+ end
75
+ end
76
+
77
+ def amotivated?
78
+ @drives.values.all? { |d| d[:level] < Constants::AMOTIVATION_THRESHOLD }
79
+ end
80
+
81
+ private
82
+
83
+ def ema(current, observed, alpha)
84
+ (current * (1.0 - alpha)) + (observed * alpha)
85
+ end
86
+
87
+ def mean(values)
88
+ return 0.0 if values.empty?
89
+
90
+ values.sum / values.size.to_f
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Motivation
8
+ module Helpers
9
+ class MotivationStore
10
+ attr_reader :drive_state, :goal_motivations
11
+
12
+ def initialize(drive_state: nil)
13
+ @drive_state = drive_state || DriveState.new
14
+ @goal_motivations = {}
15
+ @low_motivation_ticks = 0
16
+ end
17
+
18
+ def commit_goal(goal_id, drives)
19
+ valid_drives = Array(drives).select { |d| Constants::DRIVE_TYPES.include?(d) }
20
+ return false if valid_drives.empty?
21
+
22
+ trim_goals
23
+ @goal_motivations[goal_id] = {
24
+ drives: valid_drives,
25
+ energy: goal_energy_for(valid_drives),
26
+ committed: true,
27
+ committed_at: Time.now.utc
28
+ }
29
+ true
30
+ end
31
+
32
+ def release_goal(goal_id)
33
+ @goal_motivations.delete(goal_id)
34
+ true
35
+ end
36
+
37
+ def goal_energy(goal_id)
38
+ entry = @goal_motivations[goal_id]
39
+ return 0.0 unless entry
40
+
41
+ entry[:energy] = goal_energy_for(entry[:drives])
42
+ entry[:energy]
43
+ end
44
+
45
+ def most_motivated_goal
46
+ return nil if @goal_motivations.empty?
47
+
48
+ refreshed = @goal_motivations.transform_values do |entry|
49
+ entry.merge(energy: goal_energy_for(entry[:drives]))
50
+ end
51
+
52
+ best_id, best_entry = refreshed.max_by { |_, v| v[:energy] }
53
+ return nil unless best_id
54
+
55
+ { goal_id: best_id, energy: best_entry[:energy].round(4), drives: best_entry[:drives] }
56
+ end
57
+
58
+ def burnout_check
59
+ overall = @drive_state.overall_level
60
+ if overall < Constants::BURNOUT_THRESHOLD
61
+ @low_motivation_ticks += 1
62
+ else
63
+ @low_motivation_ticks = 0
64
+ end
65
+
66
+ {
67
+ burnout: @low_motivation_ticks >= 10,
68
+ low_motivation_ticks: @low_motivation_ticks,
69
+ overall_level: overall.round(4)
70
+ }
71
+ end
72
+
73
+ def stats
74
+ {
75
+ overall_level: @drive_state.overall_level.round(4),
76
+ current_mode: @drive_state.current_mode,
77
+ intrinsic_average: @drive_state.intrinsic_average.round(4),
78
+ extrinsic_average: @drive_state.extrinsic_average.round(4),
79
+ amotivated: @drive_state.amotivated?,
80
+ goal_count: @goal_motivations.size,
81
+ low_motivation_ticks: @low_motivation_ticks
82
+ }
83
+ end
84
+
85
+ private
86
+
87
+ def goal_energy_for(drives)
88
+ return 0.0 if drives.empty?
89
+
90
+ total = drives.sum { |d| @drive_state.drive_level(d) }
91
+ (total / drives.size.to_f).clamp(0.0, 1.0)
92
+ end
93
+
94
+ def trim_goals
95
+ return unless @goal_motivations.size >= Constants::MAX_GOALS
96
+
97
+ oldest_key = @goal_motivations.min_by { |_, v| v[:committed_at] }&.first
98
+ @goal_motivations.delete(oldest_key) if oldest_key
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Motivation
8
+ module Runners
9
+ module Motivation
10
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
11
+ Legion::Extensions::Helpers.const_defined?(:Lex)
12
+
13
+ def update_motivation(tick_results: {}, **)
14
+ extract_drive_signals(tick_results)
15
+ motivation_store.drive_state.decay_all
16
+ burnout = motivation_store.burnout_check
17
+ mode = motivation_store.drive_state.current_mode
18
+
19
+ Legion::Logging.debug "[motivation] mode=#{mode} " \
20
+ "overall=#{motivation_store.drive_state.overall_level.round(3)} " \
21
+ "amotivated=#{motivation_store.drive_state.amotivated?}"
22
+
23
+ {
24
+ mode: mode,
25
+ overall_level: motivation_store.drive_state.overall_level.round(4),
26
+ intrinsic_average: motivation_store.drive_state.intrinsic_average.round(4),
27
+ extrinsic_average: motivation_store.drive_state.extrinsic_average.round(4),
28
+ amotivated: motivation_store.drive_state.amotivated?,
29
+ burnout: burnout[:burnout]
30
+ }
31
+ end
32
+
33
+ def signal_drive(drive:, signal:, **)
34
+ drive_sym = drive.to_sym
35
+ return { success: false, error: "unknown drive: #{drive}" } unless Helpers::Constants::DRIVE_TYPES.include?(drive_sym)
36
+
37
+ motivation_store.drive_state.update_drive(drive_sym, signal.to_f)
38
+ level = motivation_store.drive_state.drive_level(drive_sym)
39
+
40
+ Legion::Logging.debug "[motivation] drive signal: #{drive_sym}=#{level.round(3)}"
41
+ { success: true, drive: drive_sym, level: level.round(4) }
42
+ end
43
+
44
+ def commit_to_goal(goal_id:, drives:, **)
45
+ drive_syms = Array(drives).map(&:to_sym)
46
+ result = motivation_store.commit_goal(goal_id, drive_syms)
47
+
48
+ if result
49
+ energy = motivation_store.goal_energy(goal_id)
50
+ Legion::Logging.info "[motivation] committed goal=#{goal_id} energy=#{energy.round(3)}"
51
+ { success: true, goal_id: goal_id, energy: energy.round(4) }
52
+ else
53
+ Legion::Logging.warn "[motivation] commit_goal rejected: no valid drives for #{goal_id}"
54
+ { success: false, error: 'no valid drives provided' }
55
+ end
56
+ end
57
+
58
+ def release_goal(goal_id:, **)
59
+ motivation_store.release_goal(goal_id)
60
+ Legion::Logging.debug "[motivation] released goal=#{goal_id}"
61
+ { success: true, goal_id: goal_id }
62
+ end
63
+
64
+ def motivation_for(goal_id:, **)
65
+ energy = motivation_store.goal_energy(goal_id)
66
+ Legion::Logging.debug "[motivation] motivation_for goal=#{goal_id} energy=#{energy.round(3)}"
67
+ { goal_id: goal_id, energy: energy.round(4) }
68
+ end
69
+
70
+ def most_motivated_goal(**)
71
+ result = motivation_store.most_motivated_goal
72
+ Legion::Logging.debug "[motivation] most_motivated_goal=#{result&.fetch(:goal_id, nil)}"
73
+ result || { goal_id: nil, energy: 0.0, drives: [] }
74
+ end
75
+
76
+ def drive_status(**)
77
+ drives = motivation_store.drive_state.drives.transform_values do |d|
78
+ { level: d[:level].round(4), satisfied: d[:satisfied] }
79
+ end
80
+
81
+ Legion::Logging.debug '[motivation] drive_status'
82
+ {
83
+ drives: drives,
84
+ mode: motivation_store.drive_state.current_mode,
85
+ overall: motivation_store.drive_state.overall_level.round(4)
86
+ }
87
+ end
88
+
89
+ def motivation_stats(**)
90
+ Legion::Logging.debug '[motivation] stats'
91
+ motivation_store.stats
92
+ end
93
+
94
+ private
95
+
96
+ def motivation_store
97
+ @motivation_store ||= Helpers::MotivationStore.new
98
+ end
99
+
100
+ def extract_drive_signals(tick_results)
101
+ extract_autonomy_signal(tick_results)
102
+ extract_competence_signal(tick_results)
103
+ extract_relatedness_signal(tick_results)
104
+ extract_novelty_signal(tick_results)
105
+ extract_obligation_signal(tick_results)
106
+ extract_survival_signal(tick_results)
107
+ end
108
+
109
+ def extract_autonomy_signal(tick_results)
110
+ consent_tier = tick_results.dig(:consent, :tier)
111
+ return unless consent_tier
112
+
113
+ signal = case consent_tier
114
+ when :autonomous then 1.0
115
+ when :collaborate then 0.7
116
+ when :consult then 0.4
117
+ else 0.1
118
+ end
119
+ motivation_store.drive_state.update_drive(:autonomy, signal)
120
+ end
121
+
122
+ def extract_competence_signal(tick_results)
123
+ accuracy = tick_results.dig(:prediction_engine, :accuracy)
124
+ return unless accuracy
125
+
126
+ motivation_store.drive_state.update_drive(:competence, accuracy.to_f)
127
+ end
128
+
129
+ def extract_relatedness_signal(tick_results)
130
+ trust_level = tick_results.dig(:trust, :overall_level)
131
+ return unless trust_level
132
+
133
+ motivation_store.drive_state.update_drive(:relatedness, trust_level.to_f)
134
+ end
135
+
136
+ def extract_novelty_signal(tick_results)
137
+ novel = tick_results.dig(:memory_retrieval, :novel_traces)
138
+ return unless novel
139
+
140
+ signal = novel.positive? ? [novel.to_f / 10.0, 1.0].min : 0.1
141
+ motivation_store.drive_state.update_drive(:novelty, signal)
142
+ end
143
+
144
+ def extract_obligation_signal(tick_results)
145
+ pending = tick_results.dig(:scheduler, :pending_tasks)
146
+ return unless pending
147
+
148
+ signal = pending.positive? ? [pending.to_f / 20.0, 1.0].min : 0.0
149
+ motivation_store.drive_state.update_drive(:obligation, signal)
150
+ end
151
+
152
+ def extract_survival_signal(tick_results)
153
+ extinction_level = tick_results.dig(:extinction, :level)
154
+ return unless extinction_level
155
+
156
+ signal = extinction_level.to_f / 4.0
157
+ motivation_store.drive_state.update_drive(:survival, signal)
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+ 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 Motivation
8
+ VERSION = '0.1.0'
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/affect/motivation/version'
4
+ require 'legion/extensions/agentic/affect/motivation/helpers/constants'
5
+ require 'legion/extensions/agentic/affect/motivation/helpers/drive_state'
6
+ require 'legion/extensions/agentic/affect/motivation/helpers/motivation_store'
7
+ require 'legion/extensions/agentic/affect/motivation/runners/motivation'
8
+ require 'legion/extensions/agentic/affect/motivation/client'
9
+
10
+ module Legion
11
+ module Extensions
12
+ module Agentic
13
+ module Affect
14
+ module Motivation
15
+ end
16
+ end
17
+ end
18
+ end
19
+ 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 Affect
9
+ module Reappraisal
10
+ module Actor
11
+ class AutoRegulate < Legion::Extensions::Actors::Every
12
+ def runner_class
13
+ Legion::Extensions::Agentic::Affect::Reappraisal::Runners::CognitiveReappraisal
14
+ end
15
+
16
+ def runner_function
17
+ 'regulate_pending_events'
18
+ end
19
+
20
+ def time
21
+ 300
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,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/affect/reappraisal/helpers/constants'
4
+ require 'legion/extensions/agentic/affect/reappraisal/helpers/emotional_event'
5
+ require 'legion/extensions/agentic/affect/reappraisal/helpers/reappraisal_engine'
6
+ require 'legion/extensions/agentic/affect/reappraisal/runners/cognitive_reappraisal'
7
+
8
+ module Legion
9
+ module Extensions
10
+ module Agentic
11
+ module Affect
12
+ module Reappraisal
13
+ class Client
14
+ include Runners::CognitiveReappraisal
15
+
16
+ def initialize(engine: nil, **)
17
+ @reappraisal_engine = engine || Helpers::ReappraisalEngine.new
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :reappraisal_engine
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Reappraisal
8
+ module Helpers
9
+ module Constants
10
+ MAX_EVENTS = 300
11
+ MAX_REAPPRAISALS = 500
12
+
13
+ STRATEGIES = %i[
14
+ reinterpretation
15
+ distancing
16
+ benefit_finding
17
+ normalizing
18
+ perspective_taking
19
+ temporal_distancing
20
+ ].freeze
21
+
22
+ STRATEGY_EFFECTIVENESS = {
23
+ reinterpretation: 0.25,
24
+ distancing: 0.15,
25
+ benefit_finding: 0.20,
26
+ normalizing: 0.12,
27
+ perspective_taking: 0.18,
28
+ temporal_distancing: 0.10
29
+ }.freeze
30
+
31
+ VALENCE_LABELS = {
32
+ (0.5..) => :positive,
33
+ (0.1...0.5) => :mildly_positive,
34
+ (-0.1...0.1) => :neutral,
35
+ (-0.5...-0.1) => :mildly_negative,
36
+ (..-0.5) => :negative
37
+ }.freeze
38
+
39
+ INTENSITY_LABELS = {
40
+ (0.8..) => :overwhelming,
41
+ (0.6...0.8) => :intense,
42
+ (0.4...0.6) => :moderate,
43
+ (0.2...0.4) => :mild,
44
+ (..0.2) => :faint
45
+ }.freeze
46
+
47
+ REGULATION_LABELS = {
48
+ (0.8..) => :excellent,
49
+ (0.6...0.8) => :good,
50
+ (0.4...0.6) => :moderate,
51
+ (0.2...0.4) => :poor,
52
+ (..0.2) => :minimal
53
+ }.freeze
54
+
55
+ NEGATIVE_VALENCE_THRESHOLD = -0.3
56
+ HIGH_INTENSITY_THRESHOLD = 0.7
57
+ REAPPRAISAL_DIFFICULTY_MULTIPLIER = 0.5
58
+
59
+ module_function
60
+
61
+ def label_for(value, label_map)
62
+ label_map.find { |range, _label| range.cover?(value) }&.last || :unknown
63
+ end
64
+
65
+ def valid_strategy?(strategy)
66
+ STRATEGIES.include?(strategy)
67
+ end
68
+
69
+ def clamp(value, min = -1.0, max = 1.0)
70
+ value.clamp(min, max)
71
+ end
72
+
73
+ def clamp_intensity(value)
74
+ value.clamp(0.0, 1.0)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Agentic
8
+ module Affect
9
+ module Reappraisal
10
+ module Helpers
11
+ class EmotionalEvent
12
+ include Constants
13
+
14
+ attr_reader :id, :content, :initial_valence, :current_valence,
15
+ :initial_intensity, :current_intensity, :appraisal,
16
+ :reappraisal_count, :created_at
17
+
18
+ def initialize(content:, valence:, intensity:, appraisal:)
19
+ @id = SecureRandom.uuid
20
+ @content = content
21
+ @initial_valence = Constants.clamp(valence)
22
+ @current_valence = @initial_valence
23
+ @initial_intensity = Constants.clamp_intensity(intensity)
24
+ @current_intensity = @initial_intensity
25
+ @appraisal = appraisal
26
+ @reappraisal_count = 0
27
+ @created_at = Time.now.utc
28
+ end
29
+
30
+ def reappraise!(strategy:, new_appraisal:)
31
+ return 0.0 unless Constants.valid_strategy?(strategy)
32
+
33
+ base_effectiveness = Constants::STRATEGY_EFFECTIVENESS[strategy]
34
+ difficulty = @current_intensity > Constants::HIGH_INTENSITY_THRESHOLD ? Constants::REAPPRAISAL_DIFFICULTY_MULTIPLIER : 1.0
35
+ effectiveness = (base_effectiveness * difficulty).round(10)
36
+
37
+ valence_shift = effectiveness
38
+ intensity_reduction = (effectiveness * 0.5).round(10)
39
+
40
+ old_valence = @current_valence
41
+ old_intensity = @current_intensity
42
+
43
+ @current_valence = Constants.clamp(@current_valence + valence_shift)
44
+ @current_intensity = Constants.clamp_intensity(@current_intensity - intensity_reduction)
45
+ @appraisal = new_appraisal
46
+ @reappraisal_count += 1
47
+
48
+ ((@current_valence - old_valence).abs + (old_intensity - @current_intensity).abs).round(10)
49
+ end
50
+
51
+ def negative?
52
+ @current_valence < Constants::NEGATIVE_VALENCE_THRESHOLD
53
+ end
54
+
55
+ def intense?
56
+ @current_intensity > Constants::HIGH_INTENSITY_THRESHOLD
57
+ end
58
+
59
+ def regulation_amount
60
+ valence_change = (@current_valence - @initial_valence).abs
61
+ intensity_change = (@initial_intensity - @current_intensity).abs
62
+ raw = valence_change + intensity_change
63
+ Constants.clamp_intensity(raw / 2.0)
64
+ end
65
+
66
+ def valence_label
67
+ Constants.label_for(@current_valence, Constants::VALENCE_LABELS)
68
+ end
69
+
70
+ def intensity_label
71
+ Constants.label_for(@current_intensity, Constants::INTENSITY_LABELS)
72
+ end
73
+
74
+ def to_h
75
+ {
76
+ id: @id,
77
+ content: @content,
78
+ initial_valence: @initial_valence.round(10),
79
+ current_valence: @current_valence.round(10),
80
+ initial_intensity: @initial_intensity.round(10),
81
+ current_intensity: @current_intensity.round(10),
82
+ appraisal: @appraisal,
83
+ reappraisal_count: @reappraisal_count,
84
+ negative: negative?,
85
+ intense: intense?,
86
+ regulation_amount: regulation_amount.round(10),
87
+ valence_label: valence_label,
88
+ intensity_label: intensity_label,
89
+ created_at: @created_at
90
+ }
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end