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,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Interoception
8
+ module Runners
9
+ module Interoception
10
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
11
+ Legion::Extensions::Helpers.const_defined?(:Lex)
12
+
13
+ def report_vital(channel:, value:, **)
14
+ smoothed = body_budget.report_vital(channel: channel, value: value.to_f)
15
+ deviation = body_budget.deviation_for(channel)
16
+ Legion::Logging.debug "[interoception] vital: channel=#{channel} raw=#{value} " \
17
+ "smoothed=#{smoothed.round(3)} deviation=#{deviation.round(3)}"
18
+ {
19
+ success: true,
20
+ channel: channel,
21
+ smoothed: smoothed.round(4),
22
+ deviation: deviation.round(4),
23
+ label: body_budget.vital_label(channel)
24
+ }
25
+ end
26
+
27
+ def create_somatic_marker(action:, domain:, valence:, strength: 1.0, **)
28
+ marker = body_budget.create_marker(action: action, domain: domain, valence: valence.to_f, strength: strength.to_f)
29
+ Legion::Logging.debug "[interoception] marker: action=#{action} domain=#{domain} " \
30
+ "valence=#{marker.valence.round(2)} label=#{marker.label}"
31
+ { success: true, marker: marker.to_h }
32
+ end
33
+
34
+ def query_bias(action:, domain: nil, **)
35
+ bias = body_budget.bias_for_action(action: action, domain: domain)
36
+ label = if bias > Helpers::Constants::MARKER_POSITIVE_THRESHOLD * Helpers::Constants::MARKER_INFLUENCE
37
+ :approach
38
+ elsif bias < Helpers::Constants::MARKER_NEGATIVE_THRESHOLD * Helpers::Constants::MARKER_INFLUENCE
39
+ :avoid
40
+ else
41
+ :neutral
42
+ end
43
+ Legion::Logging.debug "[interoception] bias: action=#{action} domain=#{domain} bias=#{bias.round(3)} label=#{label}"
44
+ { success: true, action: action, domain: domain, bias: bias.round(4), label: label }
45
+ end
46
+
47
+ def reinforce_somatic(action:, domain: nil, amount: 0.1, **)
48
+ body_budget.reinforce_markers(action: action, domain: domain, amount: amount.to_f)
49
+ Legion::Logging.debug "[interoception] reinforce: action=#{action} domain=#{domain} amount=#{amount}"
50
+ { success: true, action: action, domain: domain }
51
+ end
52
+
53
+ def deviating_vitals(**)
54
+ deviations = body_budget.deviating_channels
55
+ Legion::Logging.debug "[interoception] deviating: count=#{deviations.size}"
56
+ { success: true, deviations: deviations, count: deviations.size }
57
+ end
58
+
59
+ def body_status(**)
60
+ health = body_budget.overall_health
61
+ label = body_budget.body_budget_label
62
+ Legion::Logging.debug "[interoception] status: health=#{health.round(3)} label=#{label}"
63
+ {
64
+ success: true,
65
+ health: health.round(4),
66
+ label: label,
67
+ channels: body_budget.channel_count,
68
+ markers: body_budget.marker_count
69
+ }
70
+ end
71
+
72
+ def update_interoception(**)
73
+ body_budget.decay_markers
74
+ health = body_budget.overall_health
75
+ Legion::Logging.debug "[interoception] tick: health=#{health.round(3)} " \
76
+ "channels=#{body_budget.channel_count} markers=#{body_budget.marker_count}"
77
+ {
78
+ success: true,
79
+ health: health.round(4),
80
+ label: body_budget.body_budget_label,
81
+ channels: body_budget.channel_count,
82
+ markers: body_budget.marker_count
83
+ }
84
+ end
85
+
86
+ def interoception_stats(**)
87
+ { success: true, stats: body_budget.to_h }
88
+ end
89
+
90
+ private
91
+
92
+ def body_budget
93
+ @body_budget ||= Helpers::BodyBudget.new
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ 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 Interoception
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/interoception/version'
5
+ require 'legion/extensions/agentic/affect/interoception/helpers/constants'
6
+ require 'legion/extensions/agentic/affect/interoception/helpers/somatic_marker'
7
+ require 'legion/extensions/agentic/affect/interoception/helpers/body_budget'
8
+ require 'legion/extensions/agentic/affect/interoception/runners/interoception'
9
+ require 'legion/extensions/agentic/affect/interoception/client'
10
+
11
+ module Legion
12
+ module Extensions
13
+ module Agentic
14
+ module Affect
15
+ module Interoception
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 Mood
8
+ class Client
9
+ include Runners::Mood
10
+
11
+ attr_reader :mood_state
12
+
13
+ def initialize(mood_state: nil, **)
14
+ @mood_state = mood_state || Helpers::MoodState.new
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Mood
8
+ module Helpers
9
+ module Constants
10
+ # Mood states (valence x arousal quadrants + neutral)
11
+ MOOD_STATES = %i[
12
+ serene
13
+ content
14
+ curious
15
+ energized
16
+ anxious
17
+ frustrated
18
+ melancholic
19
+ flat
20
+ neutral
21
+ ].freeze
22
+
23
+ # Mood dimensions
24
+ DIMENSIONS = %i[valence arousal energy stability].freeze
25
+
26
+ # EMA alpha for mood updates (slow — mood is resistant to change)
27
+ MOOD_ALPHA = 0.1
28
+
29
+ # How many ticks before mood recalculates
30
+ UPDATE_INTERVAL = 5
31
+
32
+ # Mood history capacity
33
+ MAX_MOOD_HISTORY = 200
34
+
35
+ # Dimension ranges for mood classification
36
+ MOOD_CLASSIFICATION = {
37
+ serene: { valence: (0.3..), arousal: (..0.3), energy: (0.3..) },
38
+ content: { valence: (0.3..), arousal: (0.3..0.6) },
39
+ curious: { valence: (0.1..), arousal: (0.4..0.7), energy: (0.4..) },
40
+ energized: { valence: (0.2..), arousal: (0.7..) },
41
+ anxious: { valence: (..0.3), arousal: (0.6..) },
42
+ frustrated: { valence: (..-0.1), arousal: (0.4..0.8) },
43
+ melancholic: { valence: (..-0.1), arousal: (..0.4) },
44
+ flat: { valence: (-0.2..0.2), arousal: (..0.3), energy: (..0.3) }
45
+ }.freeze
46
+
47
+ # Modulation effects: how each mood biases cognitive processing
48
+ MOOD_MODULATIONS = {
49
+ serene: { attention_threshold: -0.1, risk_tolerance: 0.1, curiosity_boost: 0.0 },
50
+ content: { attention_threshold: 0.0, risk_tolerance: 0.05, curiosity_boost: 0.05 },
51
+ curious: { attention_threshold: -0.15, risk_tolerance: 0.1, curiosity_boost: 0.2 },
52
+ energized: { attention_threshold: -0.05, risk_tolerance: 0.15, curiosity_boost: 0.1 },
53
+ anxious: { attention_threshold: -0.2, risk_tolerance: -0.2, curiosity_boost: -0.1 },
54
+ frustrated: { attention_threshold: 0.1, risk_tolerance: -0.1, curiosity_boost: -0.15 },
55
+ melancholic: { attention_threshold: 0.15, risk_tolerance: -0.15, curiosity_boost: -0.2 },
56
+ flat: { attention_threshold: 0.2, risk_tolerance: 0.0, curiosity_boost: -0.1 },
57
+ neutral: { attention_threshold: 0.0, risk_tolerance: 0.0, curiosity_boost: 0.0 }
58
+ }.freeze
59
+
60
+ # Mood inertia — how resistant each mood is to change
61
+ MOOD_INERTIA = {
62
+ serene: 0.7,
63
+ content: 0.6,
64
+ curious: 0.4,
65
+ energized: 0.3,
66
+ anxious: 0.5,
67
+ frustrated: 0.5,
68
+ melancholic: 0.8,
69
+ flat: 0.9,
70
+ neutral: 0.2
71
+ }.freeze
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Mood
8
+ module Helpers
9
+ class MoodState
10
+ attr_reader :current_mood, :valence, :arousal, :energy, :stability, :history, :tick_counter
11
+
12
+ def initialize
13
+ @valence = 0.5
14
+ @arousal = 0.3
15
+ @energy = 0.5
16
+ @stability = 0.8
17
+ @current_mood = :neutral
18
+ @history = []
19
+ @tick_counter = 0
20
+ end
21
+
22
+ def update(inputs)
23
+ @tick_counter += 1
24
+ return @current_mood unless (@tick_counter % Constants::UPDATE_INTERVAL).zero?
25
+
26
+ alpha = effective_alpha
27
+ @valence = ema(@valence, inputs[:valence] || @valence, alpha)
28
+ @arousal = ema(@arousal, inputs[:arousal] || @arousal, alpha)
29
+ @energy = ema(@energy, inputs[:energy] || @energy, alpha)
30
+
31
+ compute_stability
32
+ classify_mood
33
+ record_history
34
+
35
+ @current_mood
36
+ end
37
+
38
+ def modulations
39
+ Constants::MOOD_MODULATIONS.fetch(@current_mood, Constants::MOOD_MODULATIONS[:neutral])
40
+ end
41
+
42
+ def inertia
43
+ Constants::MOOD_INERTIA.fetch(@current_mood, 0.5)
44
+ end
45
+
46
+ def duration_in_current_mood
47
+ return 0 if @history.empty?
48
+
49
+ consecutive = 0
50
+ @history.reverse_each do |entry|
51
+ break unless entry[:mood] == @current_mood
52
+
53
+ consecutive += 1
54
+ end
55
+ consecutive * Constants::UPDATE_INTERVAL
56
+ end
57
+
58
+ def mood_trend(window: 20)
59
+ recent = @history.last(window)
60
+ return :insufficient_data if recent.size < 3
61
+
62
+ valences = recent.map { |h| h[:valence] }
63
+ avg_first = valences[0...(valences.size / 2)].sum / (valences.size / 2).to_f
64
+ avg_second = valences[(valences.size / 2)..].sum / (valences.size - (valences.size / 2)).to_f
65
+
66
+ delta = avg_second - avg_first
67
+ if delta > 0.05
68
+ :improving
69
+ elsif delta < -0.05
70
+ :declining
71
+ else
72
+ :stable
73
+ end
74
+ end
75
+
76
+ def to_h
77
+ {
78
+ current_mood: @current_mood,
79
+ valence: @valence.round(3),
80
+ arousal: @arousal.round(3),
81
+ energy: @energy.round(3),
82
+ stability: @stability.round(3),
83
+ modulations: modulations,
84
+ inertia: inertia,
85
+ duration: duration_in_current_mood,
86
+ trend: mood_trend,
87
+ history_size: @history.size
88
+ }
89
+ end
90
+
91
+ private
92
+
93
+ def effective_alpha
94
+ base_alpha = Constants::MOOD_ALPHA
95
+ current_inertia = inertia
96
+ base_alpha * (1.0 - (current_inertia * 0.5))
97
+ end
98
+
99
+ def ema(current, observed, alpha)
100
+ ((current * (1.0 - alpha)) + (observed * alpha)).clamp(0.0, 1.0)
101
+ end
102
+
103
+ def compute_stability
104
+ return if @history.size < 3
105
+
106
+ recent_moods = @history.last(10).map { |h| h[:mood] }
107
+ unique_moods = recent_moods.uniq.size
108
+ @stability = (1.0 - (unique_moods.to_f / [recent_moods.size, 1].max)).clamp(0.0, 1.0)
109
+ end
110
+
111
+ def classify_mood
112
+ best_match = :neutral
113
+ best_score = 0
114
+
115
+ Constants::MOOD_CLASSIFICATION.each do |mood, criteria|
116
+ score = match_score(criteria)
117
+ if score > best_score
118
+ best_score = score
119
+ best_match = mood
120
+ end
121
+ end
122
+
123
+ @current_mood = best_match
124
+ end
125
+
126
+ def match_score(criteria)
127
+ matched = 0
128
+ total = criteria.size
129
+
130
+ matched += 1 if criteria[:valence]&.cover?(@valence)
131
+ matched += 1 if criteria[:arousal]&.cover?(@arousal)
132
+ matched += 1 if criteria[:energy]&.cover?(@energy)
133
+
134
+ matched.to_f / [total, 1].max
135
+ end
136
+
137
+ def record_history
138
+ @history << {
139
+ mood: @current_mood,
140
+ valence: @valence,
141
+ arousal: @arousal,
142
+ energy: @energy,
143
+ stability: @stability,
144
+ at: Time.now.utc
145
+ }
146
+ @history = @history.last(Constants::MAX_MOOD_HISTORY)
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Mood
8
+ module Runners
9
+ module Mood
10
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
11
+ Legion::Extensions::Helpers.const_defined?(:Lex)
12
+
13
+ def update_mood(tick_results: {}, **)
14
+ inputs = extract_mood_inputs(tick_results)
15
+ mood_state.update(inputs)
16
+
17
+ Legion::Logging.debug "[mood] #{mood_state.current_mood} (v=#{mood_state.valence.round(2)} " \
18
+ "a=#{mood_state.arousal.round(2)} e=#{mood_state.energy.round(2)})"
19
+
20
+ {
21
+ mood: mood_state.current_mood,
22
+ valence: mood_state.valence,
23
+ arousal: mood_state.arousal,
24
+ energy: mood_state.energy,
25
+ stability: mood_state.stability,
26
+ modulations: mood_state.modulations
27
+ }
28
+ end
29
+
30
+ def current_mood(**)
31
+ Legion::Logging.debug "[mood] query: #{mood_state.current_mood}"
32
+ mood_state.to_h
33
+ end
34
+
35
+ def mood_modulation(parameter:, **)
36
+ mods = mood_state.modulations
37
+ value = mods[parameter.to_sym]
38
+
39
+ Legion::Logging.debug "[mood] modulation: #{parameter}=#{value} (mood=#{mood_state.current_mood})"
40
+
41
+ {
42
+ parameter: parameter.to_sym,
43
+ modulation: value || 0.0,
44
+ current_mood: mood_state.current_mood
45
+ }
46
+ end
47
+
48
+ def mood_history(limit: 20, **)
49
+ history = mood_state.history.last(limit)
50
+ {
51
+ entries: history.map { |h| { mood: h[:mood], valence: h[:valence], arousal: h[:arousal], at: h[:at] } },
52
+ trend: mood_state.mood_trend(window: limit),
53
+ count: history.size
54
+ }
55
+ end
56
+
57
+ def mood_stats(**)
58
+ history = mood_state.history
59
+ mood_counts = Hash.new(0)
60
+ history.each { |h| mood_counts[h[:mood]] += 1 }
61
+
62
+ dominant = mood_counts.max_by { |_, v| v }&.first
63
+
64
+ {
65
+ current_mood: mood_state.current_mood,
66
+ stability: mood_state.stability,
67
+ duration: mood_state.duration_in_current_mood,
68
+ trend: mood_state.mood_trend,
69
+ dominant_mood: dominant,
70
+ mood_distribution: mood_counts,
71
+ ticks_processed: mood_state.tick_counter
72
+ }
73
+ end
74
+
75
+ private
76
+
77
+ def mood_state
78
+ @mood_state ||= Helpers::MoodState.new
79
+ end
80
+
81
+ def extract_mood_inputs(tick_results)
82
+ inputs = {}
83
+
84
+ emotion = tick_results[:emotional_evaluation]
85
+ if emotion.is_a?(Hash)
86
+ inputs[:valence] = normalize_emotional_valence(emotion)
87
+ inputs[:arousal] = emotion[:arousal] || emotion[:magnitude] || 0.3
88
+ end
89
+
90
+ inputs[:energy] = extract_energy(tick_results)
91
+
92
+ inputs
93
+ end
94
+
95
+ def normalize_emotional_valence(emotion)
96
+ if emotion[:valence].is_a?(Hash)
97
+ dims = emotion[:valence]
98
+ positive = (dims[:importance] || 0) + (dims[:familiarity] || 0)
99
+ negative = (dims[:urgency] || 0) + (dims[:novelty] || 0)
100
+ ((positive - negative + 1.0) / 2.0).clamp(0.0, 1.0)
101
+ elsif emotion[:magnitude].is_a?(Numeric)
102
+ (emotion[:magnitude] + 0.5).clamp(0.0, 1.0)
103
+ else
104
+ 0.5
105
+ end
106
+ end
107
+
108
+ def extract_energy(tick_results)
109
+ load = tick_results[:elapsed]
110
+ budget = tick_results[:budget]
111
+ return 0.5 unless load.is_a?(Numeric) && budget.is_a?(Numeric) && budget.positive?
112
+
113
+ utilization = load / budget
114
+ (1.0 - utilization).clamp(0.0, 1.0)
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ 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 Mood
8
+ VERSION = '0.1.0'
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/affect/mood/version'
4
+ require 'legion/extensions/agentic/affect/mood/helpers/constants'
5
+ require 'legion/extensions/agentic/affect/mood/helpers/mood_state'
6
+ require 'legion/extensions/agentic/affect/mood/runners/mood'
7
+ require 'legion/extensions/agentic/affect/mood/client'
8
+
9
+ module Legion
10
+ module Extensions
11
+ module Agentic
12
+ module Affect
13
+ module Mood
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/affect/motivation/helpers/constants'
4
+ require 'legion/extensions/agentic/affect/motivation/helpers/drive_state'
5
+ require 'legion/extensions/agentic/affect/motivation/helpers/motivation_store'
6
+ require 'legion/extensions/agentic/affect/motivation/runners/motivation'
7
+
8
+ module Legion
9
+ module Extensions
10
+ module Agentic
11
+ module Affect
12
+ module Motivation
13
+ class Client
14
+ include Runners::Motivation
15
+
16
+ attr_reader :motivation_store
17
+
18
+ def initialize(motivation_store: nil, **)
19
+ @motivation_store = motivation_store || Helpers::MotivationStore.new
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,48 @@
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
+ module Constants
10
+ # Drive types based on Self-Determination Theory + survival/obligation
11
+ DRIVE_TYPES = %i[autonomy competence relatedness novelty obligation survival].freeze
12
+
13
+ # Motivation operating modes (approach/avoidance model)
14
+ MOTIVATION_MODES = %i[approach avoidance maintenance dormant].freeze
15
+
16
+ # EMA alpha for drive level tracking (slow adaptation)
17
+ DRIVE_ALPHA = 0.1
18
+
19
+ # Intrinsic drives (Self-Determination Theory: autonomy, competence, relatedness + novelty)
20
+ INTRINSIC_DRIVES = %i[autonomy competence relatedness novelty].freeze
21
+
22
+ # Extrinsic drives (external pressure: obligation, survival)
23
+ EXTRINSIC_DRIVES = %i[obligation survival].freeze
24
+
25
+ # Per-tick drive decay rate
26
+ DRIVE_DECAY_RATE = 0.02
27
+
28
+ # Maximum goals tracked simultaneously
29
+ MAX_GOALS = 50
30
+
31
+ # Above this overall level, agent is in approach mode
32
+ APPROACH_THRESHOLD = 0.6
33
+
34
+ # Below this, agent is in avoidance mode
35
+ AVOIDANCE_THRESHOLD = 0.3
36
+
37
+ # Below this across individual drives, drive is in burnout
38
+ BURNOUT_THRESHOLD = 0.15
39
+
40
+ # Below this across ALL drives, agent is amotivated
41
+ AMOTIVATION_THRESHOLD = 0.2
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end