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,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Defusion
8
+ module Runners
9
+ module CognitiveDefusion
10
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
11
+ Legion::Extensions::Helpers.const_defined?(:Lex)
12
+
13
+ def register_thought(content:, thought_type:, belief_strength: 0.5, engine: nil, **)
14
+ eng = engine || defusion_engine
15
+ result = eng.register_thought(content: content, thought_type: thought_type, belief_strength: belief_strength)
16
+ return { success: false, **result } if result[:error]
17
+
18
+ Legion::Logging.debug "[cognitive_defusion] registered thought type=#{thought_type} " \
19
+ "belief=#{belief_strength.round(2)} id=#{result[:thought_id]}"
20
+ { success: true, **result }
21
+ end
22
+
23
+ def apply_defusion(thought_id:, technique:, engine: nil, **)
24
+ eng = engine || defusion_engine
25
+ result = eng.apply_defusion(thought_id: thought_id, technique: technique)
26
+ return { success: false, **result } if result[:error]
27
+
28
+ Legion::Logging.debug "[cognitive_defusion] defusion applied technique=#{technique} " \
29
+ "reduction=#{result[:reduction]&.round(4)} fusion=#{result[:after]&.round(4)}"
30
+ result
31
+ end
32
+
33
+ def apply_all_techniques(thought_id:, engine: nil, **)
34
+ eng = engine || defusion_engine
35
+ result = eng.apply_all_techniques(thought_id: thought_id)
36
+ return { success: false, **result } if result[:error]
37
+
38
+ Legion::Logging.debug "[cognitive_defusion] all techniques applied thought_id=#{thought_id} " \
39
+ "final_fusion=#{result[:final_fusion]&.round(4)}"
40
+ { success: true, **result }
41
+ end
42
+
43
+ def visit_thought(thought_id:, engine: nil, **)
44
+ eng = engine || defusion_engine
45
+ result = eng.visit_thought(thought_id: thought_id)
46
+ return { success: false, **result } if result[:error]
47
+
48
+ Legion::Logging.debug "[cognitive_defusion] thought visited count=#{result[:visit_count]} " \
49
+ "ruminating=#{result[:ruminating]}"
50
+ { success: true, **result }
51
+ end
52
+
53
+ def fuse_thought(thought_id:, engine: nil, **)
54
+ eng = engine || defusion_engine
55
+ thought = eng.thoughts[thought_id]
56
+ return { success: false, error: :thought_not_found } unless thought
57
+
58
+ result = thought.fuse!
59
+ Legion::Logging.debug "[cognitive_defusion] thought fused before=#{result[:before].round(4)} " \
60
+ "after=#{result[:after].round(4)}"
61
+ { success: true, thought_id: thought_id, before: result[:before], after: result[:after], enmeshed: thought.enmeshed? }
62
+ end
63
+
64
+ def enmeshed_thoughts(engine: nil, **)
65
+ eng = engine || defusion_engine
66
+ thoughts = eng.enmeshed_thoughts.map(&:to_h)
67
+ Legion::Logging.debug "[cognitive_defusion] enmeshed thoughts count=#{thoughts.size}"
68
+ { success: true, count: thoughts.size, thoughts: thoughts }
69
+ end
70
+
71
+ def defused_thoughts(engine: nil, **)
72
+ eng = engine || defusion_engine
73
+ thoughts = eng.defused_thoughts.map(&:to_h)
74
+ Legion::Logging.debug "[cognitive_defusion] defused thoughts count=#{thoughts.size}"
75
+ { success: true, count: thoughts.size, thoughts: thoughts }
76
+ end
77
+
78
+ def ruminating_thoughts(engine: nil, **)
79
+ eng = engine || defusion_engine
80
+ thoughts = eng.ruminating_thoughts.map(&:to_h)
81
+ Legion::Logging.debug "[cognitive_defusion] ruminating thoughts count=#{thoughts.size}"
82
+ { success: true, count: thoughts.size, thoughts: thoughts }
83
+ end
84
+
85
+ def most_fused(limit: 5, engine: nil, **)
86
+ eng = engine || defusion_engine
87
+ thoughts = eng.most_fused(limit: limit).map(&:to_h)
88
+ Legion::Logging.debug "[cognitive_defusion] most fused count=#{thoughts.size}"
89
+ { success: true, count: thoughts.size, thoughts: thoughts }
90
+ end
91
+
92
+ def recommend_technique(thought_id:, engine: nil, **)
93
+ eng = engine || defusion_engine
94
+ result = eng.recommend_technique(thought_id: thought_id)
95
+ return { success: false, **result } if result[:error]
96
+
97
+ Legion::Logging.debug "[cognitive_defusion] technique recommended=#{result[:technique]} " \
98
+ "for type=#{result[:thought_type]}"
99
+ { success: true, **result }
100
+ end
101
+
102
+ def defusion_report(engine: nil, **)
103
+ eng = engine || defusion_engine
104
+ report = eng.defusion_report
105
+ Legion::Logging.debug "[cognitive_defusion] report: total=#{report[:total_thoughts]} " \
106
+ "enmeshed=#{report[:enmeshed_count]} avg_fusion=#{report[:average_fusion].round(4)}"
107
+ { success: true, **report }
108
+ end
109
+
110
+ def defusion_state(engine: nil, **)
111
+ eng = engine || defusion_engine
112
+ Legion::Logging.debug '[cognitive_defusion] state queried'
113
+ { success: true, **eng.to_h }
114
+ end
115
+
116
+ private
117
+
118
+ def defusion_engine
119
+ @defusion_engine ||= Helpers::DefusionEngine.new
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+ 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 Defusion
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/defusion/version'
4
+ require 'legion/extensions/agentic/affect/defusion/helpers/constants'
5
+ require 'legion/extensions/agentic/affect/defusion/helpers/thought'
6
+ require 'legion/extensions/agentic/affect/defusion/helpers/defusion_engine'
7
+ require 'legion/extensions/agentic/affect/defusion/runners/cognitive_defusion'
8
+ require 'legion/extensions/agentic/affect/defusion/client'
9
+
10
+ module Legion
11
+ module Extensions
12
+ module Agentic
13
+ module Affect
14
+ module Defusion
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 Emotion
10
+ module Actor
11
+ class MomentumDecay < Legion::Extensions::Actors::Every
12
+ def runner_class
13
+ Legion::Extensions::Agentic::Affect::Emotion::Runners::Gut
14
+ end
15
+
16
+ def runner_function
17
+ 'decay_momentum'
18
+ end
19
+
20
+ def time
21
+ 60
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,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/affect/emotion/helpers/valence'
4
+ require 'legion/extensions/agentic/affect/emotion/helpers/baseline'
5
+ require 'legion/extensions/agentic/affect/emotion/helpers/momentum'
6
+ require 'legion/extensions/agentic/affect/emotion/runners/valence'
7
+ require 'legion/extensions/agentic/affect/emotion/runners/gut'
8
+
9
+ module Legion
10
+ module Extensions
11
+ module Agentic
12
+ module Affect
13
+ module Emotion
14
+ class Client
15
+ include Runners::Valence
16
+ include Runners::Gut
17
+
18
+ def initialize(**)
19
+ @emotion_baseline = Helpers::Baseline.new
20
+ @emotion_momentum = Helpers::Momentum.new
21
+ @domain_counts = Hash.new(0)
22
+ end
23
+
24
+ def track_domain(domain)
25
+ @domain_counts[domain] += 1
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :emotion_baseline, :emotion_momentum
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Emotion
8
+ module Helpers
9
+ class Baseline
10
+ ALPHA = 0.05 # slow adaptation to prevent adversarial manipulation
11
+ MIN_STDDEV = 0.1 # prevents division issues when baseline is stable
12
+ INITIAL_MEAN = 0.5
13
+ INITIAL_STDDEV = 0.25
14
+
15
+ attr_reader :dimensions
16
+
17
+ def initialize
18
+ @dimensions = Valence::DIMENSIONS.to_h do |dim|
19
+ [dim, { mean: INITIAL_MEAN, stddev: INITIAL_STDDEV, count: 0 }]
20
+ end
21
+ end
22
+
23
+ def normalize(raw_score, dimension)
24
+ baseline = @dimensions[dimension]
25
+ return Valence.clamp(raw_score) unless baseline
26
+
27
+ normalized = (raw_score - baseline[:mean]) / [baseline[:stddev], MIN_STDDEV].max
28
+ Valence.clamp(normalized)
29
+ end
30
+
31
+ def update(dimension, raw_score)
32
+ baseline = @dimensions[dimension]
33
+ return unless baseline
34
+
35
+ baseline[:count] += 1
36
+ old_mean = baseline[:mean]
37
+ baseline[:mean] = (ALPHA * raw_score) + ((1.0 - ALPHA) * old_mean)
38
+ # Online stddev update (Welford-like with EMA)
39
+ deviation = (raw_score - baseline[:mean]).abs
40
+ baseline[:stddev] = (ALPHA * deviation) + ((1.0 - ALPHA) * baseline[:stddev])
41
+ end
42
+
43
+ def get(dimension)
44
+ @dimensions[dimension]
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Emotion
8
+ module Helpers
9
+ class Momentum
10
+ attr_reader :valence_ema, :arousal_ema, :stability, :history
11
+
12
+ def initialize
13
+ @valence_ema = Valence.new_valence
14
+ @arousal_ema = 0.0
15
+ @stability = 1.0
16
+ @history = []
17
+ end
18
+
19
+ def update(current_valence, current_arousal)
20
+ alpha = Valence::MOMENTUM_ALPHA
21
+
22
+ previous_aggregate = Valence.magnitude(@valence_ema)
23
+ current_aggregate = Valence.magnitude(current_valence)
24
+
25
+ @valence_ema = Valence::DIMENSIONS.to_h do |dim|
26
+ [dim, (alpha * current_valence[dim]) + ((1.0 - alpha) * @valence_ema[dim])]
27
+ end
28
+
29
+ @arousal_ema = (alpha * current_arousal) + ((1.0 - alpha) * @arousal_ema)
30
+ @stability = Valence.clamp(1.0 - (current_aggregate - previous_aggregate).abs)
31
+
32
+ @history << { valence: current_valence, arousal: current_arousal, timestamp: Time.now.utc }
33
+ @history.shift while @history.size > 100
34
+
35
+ { valence_ema: @valence_ema, arousal_ema: @arousal_ema, stability: @stability }
36
+ end
37
+
38
+ def emotional_state
39
+ {
40
+ valence_ema: @valence_ema,
41
+ arousal_ema: @arousal_ema,
42
+ stability: @stability,
43
+ history_size: @history.size
44
+ }
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Emotion
8
+ module Helpers
9
+ module Valence
10
+ DIMENSIONS = %i[urgency importance novelty familiarity].freeze
11
+ MAX_MAGNITUDE = Math.sqrt(4.0) # all 4 dimensions at 1.0
12
+
13
+ # Attention modulation weights (from spec Section 6.1)
14
+ URGENCY_ATTENTION_WEIGHT = 0.4
15
+ IMPORTANCE_ATTENTION_WEIGHT = 0.35
16
+ NOVELTY_ATTENTION_WEIGHT = 0.25
17
+ ATTENTION_MULTIPLIER = 0.3
18
+
19
+ # Momentum
20
+ MOMENTUM_ALPHA = 0.3
21
+
22
+ # Source urgency map (spec Section 3.2)
23
+ SOURCE_URGENCY = {
24
+ firmware_violation: 1.0,
25
+ human_direct: 0.9,
26
+ mesh_priority: 0.7,
27
+ scheduled: 0.4,
28
+ ambient: 0.1
29
+ }.freeze
30
+
31
+ # Familiarity saturation (spec Section 3.5)
32
+ FAMILIARITY_SATURATION = 100
33
+
34
+ module_function
35
+
36
+ def new_valence(urgency: 0.0, importance: 0.0, novelty: 0.0, familiarity: 0.0)
37
+ {
38
+ urgency: clamp(urgency),
39
+ importance: clamp(importance),
40
+ novelty: clamp(novelty),
41
+ familiarity: clamp(familiarity)
42
+ }
43
+ end
44
+
45
+ def magnitude(valence)
46
+ Math.sqrt(
47
+ (valence[:urgency]**2) +
48
+ (valence[:importance]**2) +
49
+ (valence[:novelty]**2) +
50
+ (valence[:familiarity]**2)
51
+ )
52
+ end
53
+
54
+ def dominant_dimension(valence)
55
+ DIMENSIONS.max_by { |d| valence[d] }
56
+ end
57
+
58
+ def aggregate(valences)
59
+ return new_valence if valences.empty?
60
+
61
+ sums = Hash.new(0.0)
62
+ valences.each do |v|
63
+ DIMENSIONS.each { |d| sums[d] += v[d] }
64
+ end
65
+ n = valences.size.to_f
66
+ new_valence(**DIMENSIONS.to_h { |d| [d, sums[d] / n] })
67
+ end
68
+
69
+ def compute_arousal(valences)
70
+ return 0.0 if valences.empty?
71
+
72
+ total = valences.sum { |v| magnitude(v) }
73
+ clamp(total / (valences.size * MAX_MAGNITUDE))
74
+ end
75
+
76
+ def modulate_salience(base_salience, valence)
77
+ boost = ((valence[:urgency] * URGENCY_ATTENTION_WEIGHT) +
78
+ (valence[:importance] * IMPORTANCE_ATTENTION_WEIGHT) +
79
+ (valence[:novelty] * NOVELTY_ATTENTION_WEIGHT)) * ATTENTION_MULTIPLIER
80
+ clamp(base_salience + boost)
81
+ end
82
+
83
+ def clamp(value, min = 0.0, max = 1.0)
84
+ value.clamp(min, max)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Emotion
8
+ module Runners
9
+ module Gut
10
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
11
+ Legion::Extensions::Helpers.const_defined?(:Lex)
12
+
13
+ def gut_instinct(valences:, memory_signals: [], confidence_threshold: 0.5, **)
14
+ return { signal: :neutral, confidence: 0.0, basis: :insufficient_data } if valences.empty?
15
+
16
+ arousal = Helpers::Valence.compute_arousal(valences)
17
+ aggregate = Helpers::Valence.aggregate(valences)
18
+ dominant = Helpers::Valence.dominant_dimension(aggregate)
19
+
20
+ signal = determine_signal(aggregate, arousal)
21
+ confidence = compute_confidence(valences, memory_signals)
22
+
23
+ Legion::Logging.debug "[emotion] gut instinct: signal=#{signal} confidence=#{confidence.round(2)} " \
24
+ "arousal=#{arousal.round(2)} dominant=#{dominant} reliable=#{confidence >= confidence_threshold}"
25
+
26
+ result = {
27
+ signal: signal,
28
+ confidence: confidence,
29
+ arousal: arousal,
30
+ dominant: dominant,
31
+ aggregate: aggregate,
32
+ reliable: confidence >= confidence_threshold
33
+ }
34
+
35
+ # Update momentum if available
36
+ emotion_momentum.update(aggregate, arousal) if respond_to?(:emotion_momentum, true)
37
+
38
+ result
39
+ end
40
+
41
+ def decay_momentum(**)
42
+ neutral = { urgency: 0.5, importance: 0.5, novelty: 0.5, familiarity: 0.5 }
43
+ momentum = emotion_momentum
44
+ momentum.update(neutral, 0.5)
45
+ stability = momentum.stability
46
+
47
+ Legion::Logging.debug "[emotion] momentum decay: stability=#{stability.round(2)}"
48
+
49
+ { decayed: true, stability: stability }
50
+ end
51
+
52
+ def emotional_state(**)
53
+ momentum = emotion_momentum
54
+ state = momentum.emotional_state
55
+ Legion::Logging.debug "[emotion] state query: stability=#{state[:stability]&.round(2)}"
56
+ {
57
+ momentum: state,
58
+ baseline: emotion_baseline.dimensions
59
+ }
60
+ end
61
+
62
+ private
63
+
64
+ def emotion_momentum
65
+ @emotion_momentum ||= Helpers::Momentum.new
66
+ end
67
+
68
+ def determine_signal(aggregate, arousal)
69
+ if aggregate[:urgency] > 0.7 && aggregate[:importance] > 0.7
70
+ :alarm
71
+ elsif arousal > 0.7
72
+ :heightened
73
+ elsif aggregate[:novelty] > 0.7 && aggregate[:familiarity] < 0.3
74
+ :explore
75
+ elsif aggregate[:importance] > 0.6
76
+ :attend
77
+ elsif arousal < 0.2
78
+ :calm
79
+ else
80
+ :neutral
81
+ end
82
+ end
83
+
84
+ def compute_confidence(valences, memory_signals)
85
+ return 0.0 if valences.empty?
86
+
87
+ magnitudes = valences.map { |v| Helpers::Valence.magnitude(v) }
88
+ mean_mag = magnitudes.sum / magnitudes.size
89
+ variance = magnitudes.sum { |m| (m - mean_mag)**2 } / magnitudes.size
90
+
91
+ consensus = Helpers::Valence.clamp(1.0 - Math.sqrt(variance))
92
+ evidence = Helpers::Valence.clamp(memory_signals.size / 10.0)
93
+
94
+ (consensus * 0.6) + (evidence * 0.4)
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Affect
7
+ module Emotion
8
+ module Runners
9
+ module Valence
10
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
11
+ Legion::Extensions::Helpers.const_defined?(:Lex)
12
+
13
+ def evaluate_valence(signal:, source_type: :ambient, deadline: nil, domain: nil, **)
14
+ baseline = emotion_baseline
15
+
16
+ urgency_raw = compute_urgency(signal, source_type, deadline)
17
+ importance_raw = compute_importance(signal, domain)
18
+ novelty_raw = compute_novelty(signal)
19
+ familiarity_raw = compute_familiarity(domain)
20
+
21
+ valence = Helpers::Valence.new_valence(
22
+ urgency: baseline.normalize(urgency_raw, :urgency),
23
+ importance: baseline.normalize(importance_raw, :importance),
24
+ novelty: baseline.normalize(novelty_raw, :novelty),
25
+ familiarity: baseline.normalize(familiarity_raw, :familiarity)
26
+ )
27
+
28
+ # Update baselines with raw scores
29
+ Helpers::Valence::DIMENSIONS.each do |dim|
30
+ raw = { urgency: urgency_raw, importance: importance_raw,
31
+ novelty: novelty_raw, familiarity: familiarity_raw }[dim]
32
+ baseline.update(dim, raw)
33
+ end
34
+
35
+ magnitude = Helpers::Valence.magnitude(valence)
36
+ dominant = Helpers::Valence.dominant_dimension(valence)
37
+ Legion::Logging.debug "[emotion] valence: source=#{source_type} magnitude=#{magnitude.round(2)} dominant=#{dominant} " \
38
+ "u=#{valence[:urgency].round(2)} i=#{valence[:importance].round(2)} " \
39
+ "n=#{valence[:novelty].round(2)} f=#{valence[:familiarity].round(2)}"
40
+
41
+ {
42
+ valence: valence,
43
+ magnitude: magnitude,
44
+ dominant_dimension: dominant
45
+ }
46
+ end
47
+
48
+ def aggregate_valences(valences:, **)
49
+ aggregated = Helpers::Valence.aggregate(valences)
50
+ arousal = Helpers::Valence.compute_arousal(valences)
51
+ dominant = Helpers::Valence.dominant_dimension(aggregated)
52
+
53
+ Legion::Logging.debug "[emotion] aggregate: count=#{valences.size} arousal=#{arousal.round(2)} dominant=#{dominant}"
54
+ {
55
+ aggregate: aggregated,
56
+ arousal: arousal,
57
+ dominant: dominant,
58
+ count: valences.size
59
+ }
60
+ end
61
+
62
+ def modulate_attention(base_salience:, valence:, **)
63
+ modulated = Helpers::Valence.modulate_salience(base_salience, valence)
64
+ boost = modulated - base_salience
65
+ Legion::Logging.debug "[emotion] attention modulation: base=#{base_salience.round(2)} modulated=#{modulated.round(2)} boost=#{boost.round(2)}"
66
+ { original: base_salience, modulated: modulated, boost: boost }
67
+ end
68
+
69
+ def compute_arousal(valences:, **)
70
+ arousal = Helpers::Valence.compute_arousal(valences)
71
+ Legion::Logging.debug "[emotion] arousal=#{arousal.round(2)} from #{valences.size} valences"
72
+ { arousal: arousal }
73
+ end
74
+
75
+ private
76
+
77
+ def emotion_baseline
78
+ @emotion_baseline ||= Helpers::Baseline.new
79
+ end
80
+
81
+ def compute_urgency(signal, source_type, deadline)
82
+ deadline_urgency = 0.0
83
+ if deadline
84
+ remaining = [(deadline - Time.now.utc).to_f, 0.0].max
85
+ max_window = 86_400.0 # 24 hours
86
+ deadline_urgency = Helpers::Valence.clamp(1.0 - (remaining / max_window))
87
+ end
88
+
89
+ source_urgency = Helpers::Valence::SOURCE_URGENCY.fetch(source_type, 0.1)
90
+
91
+ pattern_urgency = signal.is_a?(Hash) ? (signal[:urgency_hint] || 0.0) : 0.0
92
+
93
+ (deadline_urgency * 0.5) + (source_urgency * 0.3) + (pattern_urgency * 0.2)
94
+ end
95
+
96
+ def compute_importance(signal, _domain)
97
+ domain_weight = signal.is_a?(Hash) ? (signal[:domain_weight] || 0.5) : 0.5
98
+ impact_scope = signal.is_a?(Hash) ? (signal[:impact_scope] || 0.3) : 0.3
99
+ reversibility = signal.is_a?(Hash) ? (signal[:reversibility] || 0.5) : 0.5
100
+ outcome_severity = signal.is_a?(Hash) ? (signal[:outcome_severity] || 0.3) : 0.3
101
+
102
+ (domain_weight * 0.3) + (impact_scope * 0.2) +
103
+ ((1.0 - reversibility) * 0.25) + (outcome_severity * 0.25)
104
+ end
105
+
106
+ def compute_novelty(signal)
107
+ signal.is_a?(Hash) ? (signal[:novelty_score] || 0.5) : 0.5
108
+ end
109
+
110
+ def compute_familiarity(domain)
111
+ signal_count = @domain_counts&.fetch(domain, 0) || 0
112
+ Helpers::Valence.clamp(signal_count.to_f / Helpers::Valence::FAMILIARITY_SATURATION)
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ 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 Emotion
8
+ VERSION = '0.1.1'
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end