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,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 SomaticMarker
10
+ module Actor
11
+ class Decay < Legion::Extensions::Actors::Every
12
+ def runner_class
13
+ Legion::Extensions::Agentic::Affect::SomaticMarker::Runners::SomaticMarker
14
+ end
15
+
16
+ def runner_function
17
+ 'update_somatic_markers'
18
+ end
19
+
20
+ def time
21
+ 30
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,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/affect/somatic_marker/helpers/constants'
4
+ require 'legion/extensions/agentic/affect/somatic_marker/helpers/somatic_marker'
5
+ require 'legion/extensions/agentic/affect/somatic_marker/helpers/body_state'
6
+ require 'legion/extensions/agentic/affect/somatic_marker/helpers/marker_store'
7
+ require 'legion/extensions/agentic/affect/somatic_marker/runners/somatic_marker'
8
+
9
+ module Legion
10
+ module Extensions
11
+ module Agentic
12
+ module Affect
13
+ module SomaticMarker
14
+ class Client
15
+ include Runners::SomaticMarker
16
+
17
+ def initialize(**)
18
+ @store = Helpers::MarkerStore.new
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :store
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module SomaticMarker
8
+ module Helpers
9
+ class BodyState
10
+ include Constants
11
+
12
+ attr_reader :arousal, :tension, :comfort, :gut_signal
13
+
14
+ def initialize(arousal: 0.5, tension: 0.5, comfort: 0.5, gut_signal: 0.0)
15
+ @arousal = arousal.clamp(0.0, 1.0)
16
+ @tension = tension.clamp(0.0, 1.0)
17
+ @comfort = comfort.clamp(0.0, 1.0)
18
+ @gut_signal = gut_signal.clamp(-1.0, 1.0)
19
+ end
20
+
21
+ def update(arousal: nil, tension: nil, comfort: nil, gut_signal: nil)
22
+ @arousal = arousal.clamp(0.0, 1.0) if arousal
23
+ @tension = tension.clamp(0.0, 1.0) if tension
24
+ @comfort = comfort.clamp(0.0, 1.0) if comfort
25
+ @gut_signal = gut_signal.clamp(-1.0, 1.0) if gut_signal
26
+ end
27
+
28
+ def composite_valence
29
+ (@comfort * 0.4) + ((1.0 - @tension) * 0.3) + (@gut_signal * 0.3)
30
+ end
31
+
32
+ def decay
33
+ @arousal = drift(@arousal, 0.5, BODY_STATE_DECAY)
34
+ @tension = drift(@tension, 0.5, BODY_STATE_DECAY)
35
+ @comfort = drift(@comfort, 0.5, BODY_STATE_DECAY)
36
+ @gut_signal = drift(@gut_signal, 0.0, BODY_STATE_DECAY)
37
+ end
38
+
39
+ def stressed?
40
+ @tension > 0.7 && @comfort < 0.3
41
+ end
42
+
43
+ def to_h
44
+ {
45
+ arousal: @arousal,
46
+ tension: @tension,
47
+ comfort: @comfort,
48
+ gut_signal: @gut_signal,
49
+ composite_valence: composite_valence,
50
+ stressed: stressed?
51
+ }
52
+ end
53
+
54
+ private
55
+
56
+ def drift(value, target, rate)
57
+ if value > target
58
+ (value - rate).clamp(target, 1.0)
59
+ else
60
+ (value + rate).clamp(-1.0, target)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module SomaticMarker
8
+ module Helpers
9
+ module Constants
10
+ MAX_MARKERS = 500
11
+ MAX_OPTIONS_PER_DECISION = 20
12
+ MAX_DECISION_HISTORY = 200
13
+ MARKER_DECAY = 0.01
14
+ MARKER_STRENGTH_FLOOR = 0.05
15
+ MARKER_ALPHA = 0.12
16
+ POSITIVE_BIAS = 0.6
17
+ NEGATIVE_BIAS = -0.6
18
+ DEFAULT_VALENCE = 0.0
19
+ REINFORCEMENT_BOOST = 0.15
20
+ PUNISHMENT_PENALTY = 0.2
21
+ BODY_STATE_DECAY = 0.03
22
+ MAX_BODY_STATES = 50
23
+
24
+ VALENCE_LABELS = {
25
+ (-1.0..-0.6) => :strongly_negative,
26
+ (-0.6..-0.2) => :negative,
27
+ (-0.2..0.2) => :neutral,
28
+ (0.2..0.6) => :positive,
29
+ (0.6..1.0) => :strongly_positive
30
+ }.freeze
31
+
32
+ SIGNAL_LABELS = {
33
+ approach: 'somatic signal favoring action',
34
+ avoid: 'somatic signal against action',
35
+ neutral: 'no clear somatic signal'
36
+ }.freeze
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module SomaticMarker
8
+ module Helpers
9
+ class MarkerStore
10
+ include Constants
11
+
12
+ attr_reader :markers, :body_state
13
+
14
+ def initialize
15
+ @markers = {}
16
+ @body_state = BodyState.new
17
+ @decision_history = []
18
+ @next_id = 1
19
+ end
20
+
21
+ def register_marker(action:, domain:, valence:, source: :experience)
22
+ evict_weakest if @markers.size >= MAX_MARKERS
23
+
24
+ id = generate_id
25
+ marker = SomaticMarker.new(
26
+ id: id,
27
+ action: action,
28
+ domain: domain,
29
+ valence: valence,
30
+ source: source
31
+ )
32
+ @markers[id] = marker
33
+ marker
34
+ end
35
+
36
+ def evaluate_option(action:, domain:)
37
+ relevant = markers_for(action: action, domain: domain)
38
+ return { signal: :neutral, valence: DEFAULT_VALENCE, marker_count: 0 } if relevant.empty?
39
+
40
+ weighted_valence = compute_weighted_valence(relevant)
41
+ signal = valence_to_signal(weighted_valence)
42
+ { signal: signal, valence: weighted_valence, marker_count: relevant.size }
43
+ end
44
+
45
+ def decide(options:, domain:)
46
+ capped = options.first(MAX_OPTIONS_PER_DECISION)
47
+ ranked = capped.map do |option|
48
+ eval_result = evaluate_option(action: option, domain: domain)
49
+ {
50
+ action: option,
51
+ signal: eval_result[:signal],
52
+ valence: eval_result[:valence],
53
+ marker_count: eval_result[:marker_count]
54
+ }
55
+ end
56
+
57
+ ranked.sort_by! { |r| -r[:valence] }
58
+
59
+ body_contribution = body_influence
60
+
61
+ record = {
62
+ options: capped,
63
+ ranked: ranked,
64
+ domain: domain,
65
+ body_contribution: body_contribution,
66
+ decided_at: Time.now.utc
67
+ }
68
+
69
+ @decision_history.shift while @decision_history.size >= MAX_DECISION_HISTORY
70
+ @decision_history << record
71
+
72
+ record
73
+ end
74
+
75
+ def reinforce_marker(marker_id:, outcome_valence:)
76
+ marker = @markers[marker_id]
77
+ return nil unless marker
78
+
79
+ marker.reinforce(outcome_valence: outcome_valence)
80
+ marker
81
+ end
82
+
83
+ def update_body_state(arousal: nil, tension: nil, comfort: nil, gut_signal: nil)
84
+ @body_state.update(
85
+ arousal: arousal,
86
+ tension: tension,
87
+ comfort: comfort,
88
+ gut_signal: gut_signal
89
+ )
90
+ @body_state
91
+ end
92
+
93
+ def markers_for(action:, domain:)
94
+ @markers.values.select { |m| m.action == action && m.domain == domain }
95
+ end
96
+
97
+ def body_influence
98
+ {
99
+ composite_valence: @body_state.composite_valence,
100
+ stressed: @body_state.stressed?
101
+ }
102
+ end
103
+
104
+ def decay_all
105
+ @markers.each_value(&:decay)
106
+ faded_ids = @markers.select { |_id, m| m.faded? }.keys
107
+ faded_ids.each { |id| @markers.delete(id) }
108
+ @body_state.decay
109
+ { markers_decayed: @markers.size, markers_removed: faded_ids.size }
110
+ end
111
+
112
+ def decision_history(limit: 10)
113
+ @decision_history.last(limit)
114
+ end
115
+
116
+ def to_h
117
+ {
118
+ marker_count: @markers.size,
119
+ decision_count: @decision_history.size,
120
+ body_state: @body_state.to_h,
121
+ stressed: @body_state.stressed?
122
+ }
123
+ end
124
+
125
+ private
126
+
127
+ def compute_weighted_valence(relevant)
128
+ total_strength = relevant.sum(&:strength)
129
+ return DEFAULT_VALENCE unless total_strength.positive?
130
+
131
+ relevant.sum { |m| m.valence * m.strength } / total_strength
132
+ end
133
+
134
+ def valence_to_signal(weighted_valence)
135
+ if weighted_valence > POSITIVE_BIAS
136
+ :approach
137
+ elsif weighted_valence < NEGATIVE_BIAS
138
+ :avoid
139
+ else
140
+ :neutral
141
+ end
142
+ end
143
+
144
+ def generate_id
145
+ id = "sm_#{@next_id}"
146
+ @next_id += 1
147
+ id
148
+ end
149
+
150
+ def evict_weakest
151
+ weakest_id = @markers.min_by { |_id, m| m.strength }&.first
152
+ @markers.delete(weakest_id) if weakest_id
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module SomaticMarker
8
+ module Helpers
9
+ class SomaticMarker
10
+ include Constants
11
+
12
+ attr_reader :id, :action, :domain, :valence, :strength, :source, :created_at
13
+
14
+ def initialize(id:, action:, domain:, valence:, strength: 0.5, source: :experience)
15
+ @id = id
16
+ @action = action
17
+ @domain = domain
18
+ @valence = valence.clamp(-1.0, 1.0)
19
+ @strength = strength.clamp(0.0, 1.0)
20
+ @source = source
21
+ @created_at = Time.now.utc
22
+ end
23
+
24
+ def signal
25
+ if @valence > POSITIVE_BIAS
26
+ :approach
27
+ elsif @valence < NEGATIVE_BIAS
28
+ :avoid
29
+ else
30
+ :neutral
31
+ end
32
+ end
33
+
34
+ def reinforce(outcome_valence:)
35
+ @valence = (MARKER_ALPHA * outcome_valence) + ((1.0 - MARKER_ALPHA) * @valence)
36
+ @valence = @valence.clamp(-1.0, 1.0)
37
+ @strength = (@strength + REINFORCEMENT_BOOST).clamp(0.0, 1.0)
38
+ end
39
+
40
+ def decay
41
+ @strength = (@strength - MARKER_DECAY).clamp(0.0, 1.0)
42
+ end
43
+
44
+ def faded?
45
+ @strength <= MARKER_STRENGTH_FLOOR
46
+ end
47
+
48
+ def valence_label
49
+ VALENCE_LABELS.each do |range, label|
50
+ return label if range.cover?(@valence)
51
+ end
52
+ :neutral
53
+ end
54
+
55
+ def to_h
56
+ {
57
+ id: @id,
58
+ action: @action,
59
+ domain: @domain,
60
+ valence: @valence,
61
+ strength: @strength,
62
+ source: @source,
63
+ signal: signal,
64
+ label: valence_label,
65
+ created_at: @created_at
66
+ }
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module SomaticMarker
8
+ module Runners
9
+ module SomaticMarker
10
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
11
+ Legion::Extensions::Helpers.const_defined?(:Lex)
12
+
13
+ def register_marker(action:, domain:, valence:, source: :experience, **)
14
+ marker = store.register_marker(action: action, domain: domain, valence: valence, source: source)
15
+ Legion::Logging.debug "[somatic_marker] register: action=#{action} domain=#{domain} " \
16
+ "valence=#{valence.round(3)} source=#{source} id=#{marker.id}"
17
+ { success: true, marker: marker.to_h }
18
+ rescue StandardError => e
19
+ Legion::Logging.error "[somatic_marker] register failed: #{e.message}"
20
+ { success: false, error: e.message }
21
+ end
22
+
23
+ def evaluate_option(action:, domain:, **)
24
+ result = store.evaluate_option(action: action, domain: domain)
25
+ Legion::Logging.debug "[somatic_marker] evaluate: action=#{action} domain=#{domain} " \
26
+ "signal=#{result[:signal]} valence=#{result[:valence].round(3)}"
27
+ { success: true }.merge(result)
28
+ rescue StandardError => e
29
+ Legion::Logging.error "[somatic_marker] evaluate failed: #{e.message}"
30
+ { success: false, error: e.message }
31
+ end
32
+
33
+ def make_decision(options:, domain:, **)
34
+ result = store.decide(options: options, domain: domain)
35
+ Legion::Logging.debug "[somatic_marker] decide: domain=#{domain} options=#{options.size} " \
36
+ "top=#{result[:ranked].first&.fetch(:action)}"
37
+ { success: true, decision: result }
38
+ rescue StandardError => e
39
+ Legion::Logging.error "[somatic_marker] decide failed: #{e.message}"
40
+ { success: false, error: e.message }
41
+ end
42
+
43
+ def reinforce(marker_id:, outcome_valence:, **)
44
+ marker = store.reinforce_marker(marker_id: marker_id, outcome_valence: outcome_valence)
45
+ unless marker
46
+ Legion::Logging.debug "[somatic_marker] reinforce: marker_id=#{marker_id} not found"
47
+ return { success: false, error: 'marker not found' }
48
+ end
49
+
50
+ Legion::Logging.debug "[somatic_marker] reinforce: id=#{marker_id} " \
51
+ "outcome=#{outcome_valence.round(3)} new_valence=#{marker.valence.round(3)}"
52
+ { success: true, marker: marker.to_h }
53
+ rescue StandardError => e
54
+ Legion::Logging.error "[somatic_marker] reinforce failed: #{e.message}"
55
+ { success: false, error: e.message }
56
+ end
57
+
58
+ def update_body(arousal: nil, tension: nil, comfort: nil, gut_signal: nil, **)
59
+ state = store.update_body_state(
60
+ arousal: arousal,
61
+ tension: tension,
62
+ comfort: comfort,
63
+ gut_signal: gut_signal
64
+ )
65
+ Legion::Logging.debug "[somatic_marker] body_update: composite=#{state.composite_valence.round(3)} " \
66
+ "stressed=#{state.stressed?}"
67
+ { success: true, body_state: state.to_h }
68
+ rescue StandardError => e
69
+ Legion::Logging.error "[somatic_marker] body update failed: #{e.message}"
70
+ { success: false, error: e.message }
71
+ end
72
+
73
+ def body_state(**)
74
+ state = store.body_state
75
+ Legion::Logging.debug "[somatic_marker] body_state: composite=#{state.composite_valence.round(3)}"
76
+ { success: true, body_state: state.to_h }
77
+ rescue StandardError => e
78
+ Legion::Logging.error "[somatic_marker] body_state failed: #{e.message}"
79
+ { success: false, error: e.message }
80
+ end
81
+
82
+ def markers_for_action(action:, domain:, **)
83
+ markers = store.markers_for(action: action, domain: domain)
84
+ Legion::Logging.debug "[somatic_marker] markers_for: action=#{action} domain=#{domain} " \
85
+ "count=#{markers.size}"
86
+ { success: true, markers: markers.map(&:to_h), count: markers.size }
87
+ rescue StandardError => e
88
+ Legion::Logging.error "[somatic_marker] markers_for_action failed: #{e.message}"
89
+ { success: false, error: e.message }
90
+ end
91
+
92
+ def recent_decisions(limit: 10, **)
93
+ decisions = store.decision_history(limit: limit)
94
+ Legion::Logging.debug "[somatic_marker] recent_decisions: limit=#{limit} count=#{decisions.size}"
95
+ { success: true, decisions: decisions, count: decisions.size }
96
+ rescue StandardError => e
97
+ Legion::Logging.error "[somatic_marker] recent_decisions failed: #{e.message}"
98
+ { success: false, error: e.message }
99
+ end
100
+
101
+ def update_somatic_markers(**)
102
+ result = store.decay_all
103
+ Legion::Logging.debug "[somatic_marker] decay: remaining=#{result[:markers_decayed]} " \
104
+ "removed=#{result[:markers_removed]}"
105
+ { success: true }.merge(result)
106
+ rescue StandardError => e
107
+ Legion::Logging.error "[somatic_marker] decay failed: #{e.message}"
108
+ { success: false, error: e.message }
109
+ end
110
+
111
+ def somatic_marker_stats(**)
112
+ stats = store.to_h
113
+ Legion::Logging.debug "[somatic_marker] stats: markers=#{stats[:marker_count]} " \
114
+ "decisions=#{stats[:decision_count]}"
115
+ { success: true }.merge(stats)
116
+ rescue StandardError => e
117
+ Legion::Logging.error "[somatic_marker] stats failed: #{e.message}"
118
+ { success: false, error: e.message }
119
+ end
120
+
121
+ private
122
+
123
+ def store
124
+ @store ||= Helpers::MarkerStore.new
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+ 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 SomaticMarker
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 'legion/extensions/agentic/affect/somatic_marker/version'
4
+ require 'legion/extensions/agentic/affect/somatic_marker/helpers/constants'
5
+ require 'legion/extensions/agentic/affect/somatic_marker/helpers/somatic_marker'
6
+ require 'legion/extensions/agentic/affect/somatic_marker/helpers/body_state'
7
+ require 'legion/extensions/agentic/affect/somatic_marker/helpers/marker_store'
8
+ require 'legion/extensions/agentic/affect/somatic_marker/runners/somatic_marker'
9
+ require 'legion/extensions/agentic/affect/somatic_marker/client'
10
+
11
+ module Legion
12
+ module Extensions
13
+ module Agentic
14
+ module Affect
15
+ module SomaticMarker
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ VERSION = '0.1.0'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'affect/version'
4
+ require_relative 'affect/cognitive_empathy'
5
+ require_relative 'affect/reappraisal'
6
+ require_relative 'affect/defusion'
7
+ require_relative 'affect/contagion'
8
+ require_relative 'affect/emotion'
9
+ require_relative 'affect/regulation'
10
+ require_relative 'affect/mood'
11
+ require_relative 'affect/appraisal'
12
+ require_relative 'affect/empathy'
13
+ require_relative 'affect/somatic_marker'
14
+ require_relative 'affect/interoception'
15
+ require_relative 'affect/flow'
16
+ require_relative 'affect/fatigue'
17
+ require_relative 'affect/motivation'
18
+ require_relative 'affect/reward'
19
+ require_relative 'affect/resilience'
20
+ require_relative 'affect/resonance'
21
+
22
+ module Legion
23
+ module Extensions
24
+ module Agentic
25
+ module Affect
26
+ extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
27
+
28
+ def remote_invocable?
29
+ false
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/affect/appraisal/client'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Affect::Appraisal::Client do
6
+ let(:client) { described_class.new }
7
+
8
+ it 'responds to all runner methods' do
9
+ expect(client).to respond_to(:appraise_event)
10
+ expect(client).to respond_to(:reappraise_event)
11
+ expect(client).to respond_to(:select_coping_strategy)
12
+ expect(client).to respond_to(:add_coping_strategy)
13
+ expect(client).to respond_to(:evaluate_coping)
14
+ expect(client).to respond_to(:emotional_pattern)
15
+ expect(client).to respond_to(:update_appraisal)
16
+ expect(client).to respond_to(:appraisal_stats)
17
+ end
18
+
19
+ it 'maintains isolated engine state per client instance' do
20
+ client2 = described_class.new
21
+ client.appraise_event(
22
+ event: 'only in client1',
23
+ primary: { relevance: 0.9, goal_congruence: 0.9, goal_importance: 0.8 },
24
+ secondary: { coping_potential: 0.8, control_expectation: 0.7, future_expectancy: 0.6 }
25
+ )
26
+ expect(client.appraisal_stats[:total]).to eq(1)
27
+ expect(client2.appraisal_stats[:total]).to eq(0)
28
+ end
29
+
30
+ it 'runs a full appraisal cycle' do
31
+ primary = { relevance: 0.9, goal_congruence: 0.3, goal_importance: 0.8 }
32
+ secondary = { coping_potential: 0.2, control_expectation: 0.3, future_expectancy: 0.4 }
33
+
34
+ appraisal_id = client.appraise_event(event: 'crisis', primary: primary,
35
+ secondary: secondary)[:appraisal][:id]
36
+ client.add_coping_strategy(name: 'deep_breathing', coping_type: :emotion_focused, effectiveness: 0.7)
37
+ client.select_coping_strategy(appraisal_id: appraisal_id, coping_type: :emotion_focused)
38
+ eval_result = client.evaluate_coping(appraisal_id: appraisal_id)
39
+ expect(eval_result[:success]).to be(true)
40
+ expect(eval_result[:coping]).to eq('deep_breathing')
41
+
42
+ client.reappraise_event(
43
+ appraisal_id: appraisal_id,
44
+ new_primary: { relevance: 0.9, goal_congruence: 0.9, goal_importance: 0.8 },
45
+ new_secondary: { coping_potential: 0.8, control_expectation: 0.7, future_expectancy: 0.6 }
46
+ )
47
+ client.update_appraisal
48
+ stats = client.appraisal_stats
49
+ expect(stats[:total]).to eq(1)
50
+ expect(stats[:pattern]).to be_a(Hash)
51
+ end
52
+ end