lex-agentic-social 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 (235) 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-social.gemspec +30 -0
  7. data/lib/legion/extensions/agentic/social/apprenticeship/client.rb +28 -0
  8. data/lib/legion/extensions/agentic/social/apprenticeship/helpers/apprenticeship.rb +90 -0
  9. data/lib/legion/extensions/agentic/social/apprenticeship/helpers/apprenticeship_engine.rb +109 -0
  10. data/lib/legion/extensions/agentic/social/apprenticeship/helpers/apprenticeship_model.rb +93 -0
  11. data/lib/legion/extensions/agentic/social/apprenticeship/runners/cognitive_apprenticeship.rb +112 -0
  12. data/lib/legion/extensions/agentic/social/apprenticeship/version.rb +13 -0
  13. data/lib/legion/extensions/agentic/social/apprenticeship.rb +19 -0
  14. data/lib/legion/extensions/agentic/social/conflict/actors/stale_check.rb +45 -0
  15. data/lib/legion/extensions/agentic/social/conflict/client.rb +27 -0
  16. data/lib/legion/extensions/agentic/social/conflict/helpers/conflict_log.rb +70 -0
  17. data/lib/legion/extensions/agentic/social/conflict/helpers/llm_enhancer.rb +130 -0
  18. data/lib/legion/extensions/agentic/social/conflict/helpers/severity.rb +47 -0
  19. data/lib/legion/extensions/agentic/social/conflict/runners/conflict.rb +112 -0
  20. data/lib/legion/extensions/agentic/social/conflict/version.rb +13 -0
  21. data/lib/legion/extensions/agentic/social/conflict.rb +19 -0
  22. data/lib/legion/extensions/agentic/social/conscience/client.rb +26 -0
  23. data/lib/legion/extensions/agentic/social/conscience/helpers/constants.rb +53 -0
  24. data/lib/legion/extensions/agentic/social/conscience/helpers/moral_evaluator.rb +178 -0
  25. data/lib/legion/extensions/agentic/social/conscience/helpers/moral_store.rb +116 -0
  26. data/lib/legion/extensions/agentic/social/conscience/runners/conscience.rb +117 -0
  27. data/lib/legion/extensions/agentic/social/conscience/version.rb +13 -0
  28. data/lib/legion/extensions/agentic/social/conscience.rb +19 -0
  29. data/lib/legion/extensions/agentic/social/consent/actors/tier_evaluation.rb +45 -0
  30. data/lib/legion/extensions/agentic/social/consent/client.rb +27 -0
  31. data/lib/legion/extensions/agentic/social/consent/helpers/consent_map.rb +199 -0
  32. data/lib/legion/extensions/agentic/social/consent/helpers/tiers.rb +54 -0
  33. data/lib/legion/extensions/agentic/social/consent/local_migrations/20260316000010_create_consent_domains.rb +16 -0
  34. data/lib/legion/extensions/agentic/social/consent/runners/consent.rb +228 -0
  35. data/lib/legion/extensions/agentic/social/consent/version.rb +13 -0
  36. data/lib/legion/extensions/agentic/social/consent.rb +25 -0
  37. data/lib/legion/extensions/agentic/social/entrainment/client.rb +19 -0
  38. data/lib/legion/extensions/agentic/social/entrainment/helpers/constants.rb +40 -0
  39. data/lib/legion/extensions/agentic/social/entrainment/helpers/entrainment_engine.rb +120 -0
  40. data/lib/legion/extensions/agentic/social/entrainment/helpers/pairing.rb +86 -0
  41. data/lib/legion/extensions/agentic/social/entrainment/runners/cognitive_entrainment.rb +89 -0
  42. data/lib/legion/extensions/agentic/social/entrainment/version.rb +13 -0
  43. data/lib/legion/extensions/agentic/social/entrainment.rb +19 -0
  44. data/lib/legion/extensions/agentic/social/governance/actors/shadow_ai_scan.rb +19 -0
  45. data/lib/legion/extensions/agentic/social/governance/actors/vote_timeout.rb +45 -0
  46. data/lib/legion/extensions/agentic/social/governance/client.rb +27 -0
  47. data/lib/legion/extensions/agentic/social/governance/helpers/layers.rb +40 -0
  48. data/lib/legion/extensions/agentic/social/governance/helpers/proposal.rb +94 -0
  49. data/lib/legion/extensions/agentic/social/governance/runners/governance.rb +87 -0
  50. data/lib/legion/extensions/agentic/social/governance/runners/shadow_ai.rb +93 -0
  51. data/lib/legion/extensions/agentic/social/governance/version.rb +13 -0
  52. data/lib/legion/extensions/agentic/social/governance.rb +20 -0
  53. data/lib/legion/extensions/agentic/social/joint_attention/actors/decay.rb +45 -0
  54. data/lib/legion/extensions/agentic/social/joint_attention/client.rb +28 -0
  55. data/lib/legion/extensions/agentic/social/joint_attention/helpers/attention_target.rb +124 -0
  56. data/lib/legion/extensions/agentic/social/joint_attention/helpers/constants.rb +34 -0
  57. data/lib/legion/extensions/agentic/social/joint_attention/helpers/joint_focus_manager.rb +157 -0
  58. data/lib/legion/extensions/agentic/social/joint_attention/runners/joint_attention.rb +88 -0
  59. data/lib/legion/extensions/agentic/social/joint_attention/version.rb +13 -0
  60. data/lib/legion/extensions/agentic/social/joint_attention.rb +20 -0
  61. data/lib/legion/extensions/agentic/social/mentalizing/actors/decay.rb +45 -0
  62. data/lib/legion/extensions/agentic/social/mentalizing/client.rb +28 -0
  63. data/lib/legion/extensions/agentic/social/mentalizing/helpers/belief_attribution.rb +58 -0
  64. data/lib/legion/extensions/agentic/social/mentalizing/helpers/constants.rb +33 -0
  65. data/lib/legion/extensions/agentic/social/mentalizing/helpers/mental_model.rb +137 -0
  66. data/lib/legion/extensions/agentic/social/mentalizing/runners/mentalizing.rb +93 -0
  67. data/lib/legion/extensions/agentic/social/mentalizing/version.rb +13 -0
  68. data/lib/legion/extensions/agentic/social/mentalizing.rb +20 -0
  69. data/lib/legion/extensions/agentic/social/mirror/client.rb +33 -0
  70. data/lib/legion/extensions/agentic/social/mirror/helpers/constants.rb +65 -0
  71. data/lib/legion/extensions/agentic/social/mirror/helpers/mirror_engine.rb +132 -0
  72. data/lib/legion/extensions/agentic/social/mirror/helpers/mirror_event.rb +46 -0
  73. data/lib/legion/extensions/agentic/social/mirror/helpers/simulation.rb +43 -0
  74. data/lib/legion/extensions/agentic/social/mirror/runners/observe.rb +52 -0
  75. data/lib/legion/extensions/agentic/social/mirror/runners/resonance.rb +79 -0
  76. data/lib/legion/extensions/agentic/social/mirror/runners/simulate.rb +63 -0
  77. data/lib/legion/extensions/agentic/social/mirror/version.rb +13 -0
  78. data/lib/legion/extensions/agentic/social/mirror.rb +22 -0
  79. data/lib/legion/extensions/agentic/social/mirror_system/actors/decay.rb +45 -0
  80. data/lib/legion/extensions/agentic/social/mirror_system/client.rb +28 -0
  81. data/lib/legion/extensions/agentic/social/mirror_system/helpers/constants.rb +62 -0
  82. data/lib/legion/extensions/agentic/social/mirror_system/helpers/mirror_system.rb +162 -0
  83. data/lib/legion/extensions/agentic/social/mirror_system/helpers/observed_behavior.rb +67 -0
  84. data/lib/legion/extensions/agentic/social/mirror_system/runners/mirror.rb +99 -0
  85. data/lib/legion/extensions/agentic/social/mirror_system/version.rb +13 -0
  86. data/lib/legion/extensions/agentic/social/mirror_system.rb +20 -0
  87. data/lib/legion/extensions/agentic/social/moral_reasoning/client.rb +19 -0
  88. data/lib/legion/extensions/agentic/social/moral_reasoning/helpers/constants.rb +49 -0
  89. data/lib/legion/extensions/agentic/social/moral_reasoning/helpers/dilemma.rb +68 -0
  90. data/lib/legion/extensions/agentic/social/moral_reasoning/helpers/llm_enhancer.rb +140 -0
  91. data/lib/legion/extensions/agentic/social/moral_reasoning/helpers/moral_engine.rb +239 -0
  92. data/lib/legion/extensions/agentic/social/moral_reasoning/helpers/moral_foundation.rb +45 -0
  93. data/lib/legion/extensions/agentic/social/moral_reasoning/runners/moral_reasoning.rb +121 -0
  94. data/lib/legion/extensions/agentic/social/moral_reasoning/version.rb +13 -0
  95. data/lib/legion/extensions/agentic/social/moral_reasoning.rb +21 -0
  96. data/lib/legion/extensions/agentic/social/perspective_shifting/client.rb +29 -0
  97. data/lib/legion/extensions/agentic/social/perspective_shifting/helpers/constants.rb +67 -0
  98. data/lib/legion/extensions/agentic/social/perspective_shifting/helpers/perspective.rb +45 -0
  99. data/lib/legion/extensions/agentic/social/perspective_shifting/helpers/perspective_view.rb +57 -0
  100. data/lib/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine.rb +166 -0
  101. data/lib/legion/extensions/agentic/social/perspective_shifting/runners/perspective_shifting.rb +167 -0
  102. data/lib/legion/extensions/agentic/social/perspective_shifting/version.rb +13 -0
  103. data/lib/legion/extensions/agentic/social/perspective_shifting.rb +20 -0
  104. data/lib/legion/extensions/agentic/social/social/client.rb +25 -0
  105. data/lib/legion/extensions/agentic/social/social/helpers/constants.rb +84 -0
  106. data/lib/legion/extensions/agentic/social/social/helpers/social_graph.rb +172 -0
  107. data/lib/legion/extensions/agentic/social/social/runners/social.rb +146 -0
  108. data/lib/legion/extensions/agentic/social/social/version.rb +13 -0
  109. data/lib/legion/extensions/agentic/social/social.rb +18 -0
  110. data/lib/legion/extensions/agentic/social/social_learning/client.rb +25 -0
  111. data/lib/legion/extensions/agentic/social/social_learning/helpers/constants.rb +42 -0
  112. data/lib/legion/extensions/agentic/social/social_learning/helpers/model_agent.rb +82 -0
  113. data/lib/legion/extensions/agentic/social/social_learning/helpers/observed_behavior.rb +61 -0
  114. data/lib/legion/extensions/agentic/social/social_learning/helpers/social_learning_engine.rb +134 -0
  115. data/lib/legion/extensions/agentic/social/social_learning/runners/social_learning.rb +105 -0
  116. data/lib/legion/extensions/agentic/social/social_learning/version.rb +13 -0
  117. data/lib/legion/extensions/agentic/social/social_learning.rb +20 -0
  118. data/lib/legion/extensions/agentic/social/symbiosis/client.rb +23 -0
  119. data/lib/legion/extensions/agentic/social/symbiosis/helpers/constants.rb +50 -0
  120. data/lib/legion/extensions/agentic/social/symbiosis/helpers/ecosystem.rb +113 -0
  121. data/lib/legion/extensions/agentic/social/symbiosis/helpers/symbiosis_engine.rb +104 -0
  122. data/lib/legion/extensions/agentic/social/symbiosis/helpers/symbiotic_bond.rb +112 -0
  123. data/lib/legion/extensions/agentic/social/symbiosis/runners/cognitive_symbiosis.rb +101 -0
  124. data/lib/legion/extensions/agentic/social/symbiosis/version.rb +13 -0
  125. data/lib/legion/extensions/agentic/social/symbiosis.rb +22 -0
  126. data/lib/legion/extensions/agentic/social/theory_of_mind/client.rb +26 -0
  127. data/lib/legion/extensions/agentic/social/theory_of_mind/helpers/agent_model.rb +173 -0
  128. data/lib/legion/extensions/agentic/social/theory_of_mind/helpers/constants.rb +70 -0
  129. data/lib/legion/extensions/agentic/social/theory_of_mind/helpers/mental_state_tracker.rb +169 -0
  130. data/lib/legion/extensions/agentic/social/theory_of_mind/runners/theory_of_mind.rb +159 -0
  131. data/lib/legion/extensions/agentic/social/theory_of_mind/version.rb +13 -0
  132. data/lib/legion/extensions/agentic/social/theory_of_mind.rb +19 -0
  133. data/lib/legion/extensions/agentic/social/trust/actors/decay.rb +45 -0
  134. data/lib/legion/extensions/agentic/social/trust/client.rb +27 -0
  135. data/lib/legion/extensions/agentic/social/trust/helpers/trust_map.rb +160 -0
  136. data/lib/legion/extensions/agentic/social/trust/helpers/trust_model.rb +52 -0
  137. data/lib/legion/extensions/agentic/social/trust/local_migrations/20260316000020_create_trust_entries.rb +23 -0
  138. data/lib/legion/extensions/agentic/social/trust/runners/trust.rb +80 -0
  139. data/lib/legion/extensions/agentic/social/trust/version.rb +13 -0
  140. data/lib/legion/extensions/agentic/social/trust.rb +25 -0
  141. data/lib/legion/extensions/agentic/social/version.rb +11 -0
  142. data/lib/legion/extensions/agentic/social.rb +34 -0
  143. data/spec/legion/extensions/agentic/social/apprenticeship/client_spec.rb +20 -0
  144. data/spec/legion/extensions/agentic/social/apprenticeship/cognitive_apprenticeship_spec.rb +11 -0
  145. data/spec/legion/extensions/agentic/social/apprenticeship/helpers/apprenticeship_engine_spec.rb +146 -0
  146. data/spec/legion/extensions/agentic/social/apprenticeship/helpers/apprenticeship_model_spec.rb +124 -0
  147. data/spec/legion/extensions/agentic/social/apprenticeship/helpers/apprenticeship_spec.rb +136 -0
  148. data/spec/legion/extensions/agentic/social/apprenticeship/runners/cognitive_apprenticeship_spec.rb +154 -0
  149. data/spec/legion/extensions/agentic/social/conflict/actors/stale_check_spec.rb +45 -0
  150. data/spec/legion/extensions/agentic/social/conflict/client_spec.rb +15 -0
  151. data/spec/legion/extensions/agentic/social/conflict/helpers/conflict_log_spec.rb +232 -0
  152. data/spec/legion/extensions/agentic/social/conflict/helpers/llm_enhancer_spec.rb +189 -0
  153. data/spec/legion/extensions/agentic/social/conflict/helpers/severity_spec.rb +215 -0
  154. data/spec/legion/extensions/agentic/social/conflict/runners/conflict_spec.rb +151 -0
  155. data/spec/legion/extensions/agentic/social/conscience/client_spec.rb +58 -0
  156. data/spec/legion/extensions/agentic/social/conscience/helpers/constants_spec.rb +124 -0
  157. data/spec/legion/extensions/agentic/social/conscience/helpers/moral_evaluator_spec.rb +253 -0
  158. data/spec/legion/extensions/agentic/social/conscience/helpers/moral_store_spec.rb +230 -0
  159. data/spec/legion/extensions/agentic/social/conscience/runners/conscience_spec.rb +239 -0
  160. data/spec/legion/extensions/agentic/social/consent/actors/tier_evaluation_spec.rb +46 -0
  161. data/spec/legion/extensions/agentic/social/consent/client_spec.rb +33 -0
  162. data/spec/legion/extensions/agentic/social/consent/helpers/tiers_spec.rb +49 -0
  163. data/spec/legion/extensions/agentic/social/consent/local_persistence_spec.rb +234 -0
  164. data/spec/legion/extensions/agentic/social/consent/runners/consent_spec.rb +224 -0
  165. data/spec/legion/extensions/agentic/social/entrainment/client_spec.rb +21 -0
  166. data/spec/legion/extensions/agentic/social/entrainment/helpers/entrainment_engine_spec.rb +116 -0
  167. data/spec/legion/extensions/agentic/social/entrainment/helpers/pairing_spec.rb +103 -0
  168. data/spec/legion/extensions/agentic/social/entrainment/runners/cognitive_entrainment_spec.rb +87 -0
  169. data/spec/legion/extensions/agentic/social/governance/actors/vote_timeout_spec.rb +45 -0
  170. data/spec/legion/extensions/agentic/social/governance/client_spec.rb +14 -0
  171. data/spec/legion/extensions/agentic/social/governance/helpers/layers_spec.rb +190 -0
  172. data/spec/legion/extensions/agentic/social/governance/helpers/proposal_spec.rb +188 -0
  173. data/spec/legion/extensions/agentic/social/governance/runners/governance_spec.rb +101 -0
  174. data/spec/legion/extensions/agentic/social/governance/runners/shadow_ai_spec.rb +65 -0
  175. data/spec/legion/extensions/agentic/social/joint_attention/client_spec.rb +36 -0
  176. data/spec/legion/extensions/agentic/social/joint_attention/helpers/attention_target_spec.rb +258 -0
  177. data/spec/legion/extensions/agentic/social/joint_attention/helpers/joint_focus_manager_spec.rb +238 -0
  178. data/spec/legion/extensions/agentic/social/joint_attention/runners/joint_attention_spec.rb +228 -0
  179. data/spec/legion/extensions/agentic/social/mentalizing/client_spec.rb +19 -0
  180. data/spec/legion/extensions/agentic/social/mentalizing/helpers/belief_attribution_spec.rb +108 -0
  181. data/spec/legion/extensions/agentic/social/mentalizing/helpers/mental_model_spec.rb +179 -0
  182. data/spec/legion/extensions/agentic/social/mentalizing/runners/mentalizing_spec.rb +162 -0
  183. data/spec/legion/extensions/agentic/social/mirror/client_spec.rb +92 -0
  184. data/spec/legion/extensions/agentic/social/mirror/helpers/constants_spec.rb +123 -0
  185. data/spec/legion/extensions/agentic/social/mirror/helpers/mirror_engine_spec.rb +217 -0
  186. data/spec/legion/extensions/agentic/social/mirror/helpers/mirror_event_spec.rb +102 -0
  187. data/spec/legion/extensions/agentic/social/mirror/helpers/simulation_spec.rb +100 -0
  188. data/spec/legion/extensions/agentic/social/mirror/runners/observe_spec.rb +77 -0
  189. data/spec/legion/extensions/agentic/social/mirror/runners/resonance_spec.rb +123 -0
  190. data/spec/legion/extensions/agentic/social/mirror/runners/simulate_spec.rb +103 -0
  191. data/spec/legion/extensions/agentic/social/mirror_system/client_spec.rb +40 -0
  192. data/spec/legion/extensions/agentic/social/mirror_system/helpers/mirror_system_spec.rb +144 -0
  193. data/spec/legion/extensions/agentic/social/mirror_system/helpers/observed_behavior_spec.rb +98 -0
  194. data/spec/legion/extensions/agentic/social/mirror_system/runners/mirror_spec.rb +122 -0
  195. data/spec/legion/extensions/agentic/social/moral_reasoning/client_spec.rb +34 -0
  196. data/spec/legion/extensions/agentic/social/moral_reasoning/helpers/dilemma_spec.rb +108 -0
  197. data/spec/legion/extensions/agentic/social/moral_reasoning/helpers/llm_enhancer_spec.rb +232 -0
  198. data/spec/legion/extensions/agentic/social/moral_reasoning/helpers/moral_engine_spec.rb +266 -0
  199. data/spec/legion/extensions/agentic/social/moral_reasoning/helpers/moral_foundation_spec.rb +70 -0
  200. data/spec/legion/extensions/agentic/social/moral_reasoning/runners/moral_reasoning_spec.rb +275 -0
  201. data/spec/legion/extensions/agentic/social/perspective_shifting/client_spec.rb +30 -0
  202. data/spec/legion/extensions/agentic/social/perspective_shifting/helpers/constants_spec.rb +99 -0
  203. data/spec/legion/extensions/agentic/social/perspective_shifting/helpers/perspective_spec.rb +77 -0
  204. data/spec/legion/extensions/agentic/social/perspective_shifting/helpers/perspective_view_spec.rb +105 -0
  205. data/spec/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine_spec.rb +246 -0
  206. data/spec/legion/extensions/agentic/social/perspective_shifting/runners/perspective_shifting_spec.rb +277 -0
  207. data/spec/legion/extensions/agentic/social/social/client_spec.rb +72 -0
  208. data/spec/legion/extensions/agentic/social/social/helpers/constants_spec.rb +99 -0
  209. data/spec/legion/extensions/agentic/social/social/helpers/social_graph_spec.rb +322 -0
  210. data/spec/legion/extensions/agentic/social/social/runners/social_spec.rb +220 -0
  211. data/spec/legion/extensions/agentic/social/social_learning/client_spec.rb +25 -0
  212. data/spec/legion/extensions/agentic/social/social_learning/helpers/constants_spec.rb +44 -0
  213. data/spec/legion/extensions/agentic/social/social_learning/helpers/model_agent_spec.rb +120 -0
  214. data/spec/legion/extensions/agentic/social/social_learning/helpers/observed_behavior_spec.rb +81 -0
  215. data/spec/legion/extensions/agentic/social/social_learning/helpers/social_learning_engine_spec.rb +196 -0
  216. data/spec/legion/extensions/agentic/social/social_learning/runners/social_learning_spec.rb +150 -0
  217. data/spec/legion/extensions/agentic/social/symbiosis/client_spec.rb +45 -0
  218. data/spec/legion/extensions/agentic/social/symbiosis/helpers/constants_spec.rb +73 -0
  219. data/spec/legion/extensions/agentic/social/symbiosis/helpers/ecosystem_spec.rb +185 -0
  220. data/spec/legion/extensions/agentic/social/symbiosis/helpers/symbiosis_engine_spec.rb +182 -0
  221. data/spec/legion/extensions/agentic/social/symbiosis/helpers/symbiotic_bond_spec.rb +209 -0
  222. data/spec/legion/extensions/agentic/social/symbiosis/runners/cognitive_symbiosis_spec.rb +182 -0
  223. data/spec/legion/extensions/agentic/social/theory_of_mind/client_spec.rb +63 -0
  224. data/spec/legion/extensions/agentic/social/theory_of_mind/helpers/agent_model_spec.rb +244 -0
  225. data/spec/legion/extensions/agentic/social/theory_of_mind/helpers/constants_spec.rb +71 -0
  226. data/spec/legion/extensions/agentic/social/theory_of_mind/helpers/mental_state_tracker_spec.rb +228 -0
  227. data/spec/legion/extensions/agentic/social/theory_of_mind/runners/theory_of_mind_spec.rb +221 -0
  228. data/spec/legion/extensions/agentic/social/trust/actors/decay_spec.rb +62 -0
  229. data/spec/legion/extensions/agentic/social/trust/client_spec.rb +17 -0
  230. data/spec/legion/extensions/agentic/social/trust/helpers/trust_map_spec.rb +299 -0
  231. data/spec/legion/extensions/agentic/social/trust/helpers/trust_model_spec.rb +179 -0
  232. data/spec/legion/extensions/agentic/social/trust/local_persistence_spec.rb +359 -0
  233. data/spec/legion/extensions/agentic/social/trust/runners/trust_spec.rb +84 -0
  234. data/spec/spec_helper.rb +54 -0
  235. metadata +319 -0
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Agentic
8
+ module Social
9
+ module Conflict
10
+ module Helpers
11
+ class ConflictLog
12
+ attr_reader :conflicts
13
+
14
+ def initialize
15
+ @conflicts = {}
16
+ end
17
+
18
+ def record(parties:, severity:, description:, posture: nil)
19
+ id = SecureRandom.uuid
20
+ @conflicts[id] = {
21
+ conflict_id: id,
22
+ parties: parties,
23
+ severity: severity,
24
+ posture: posture || Severity.recommended_posture(severity),
25
+ description: description,
26
+ status: :active,
27
+ outcome: nil,
28
+ created_at: Time.now.utc,
29
+ resolved_at: nil,
30
+ exchanges: []
31
+ }
32
+ id
33
+ end
34
+
35
+ def add_exchange(conflict_id, speaker:, message:)
36
+ conflict = @conflicts[conflict_id]
37
+ return nil unless conflict
38
+
39
+ conflict[:exchanges] << { speaker: speaker, message: message, at: Time.now.utc }
40
+ end
41
+
42
+ def resolve(conflict_id, outcome:, resolution_notes: nil)
43
+ conflict = @conflicts[conflict_id]
44
+ return nil unless conflict
45
+
46
+ conflict[:status] = :resolved
47
+ conflict[:outcome] = outcome
48
+ conflict[:resolution_notes] = resolution_notes
49
+ conflict[:resolved_at] = Time.now.utc
50
+ conflict
51
+ end
52
+
53
+ def active_conflicts
54
+ @conflicts.values.select { |c| c[:status] == :active }
55
+ end
56
+
57
+ def get(conflict_id)
58
+ @conflicts[conflict_id]
59
+ end
60
+
61
+ def count
62
+ @conflicts.size
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Social
7
+ module Conflict
8
+ module Helpers
9
+ module LlmEnhancer
10
+ SYSTEM_PROMPT = <<~PROMPT
11
+ You are the conflict mediation processor for an autonomous AI agent built on LegionIO.
12
+ You analyze disagreements between the agent and human partners, then suggest resolution approaches.
13
+ Be neutral, constructive, and specific. Focus on finding common ground and actionable next steps.
14
+ Do not take sides. Identify the underlying needs behind each position.
15
+ PROMPT
16
+
17
+ module_function
18
+
19
+ def available?
20
+ defined?(Legion::LLM) && Legion::LLM.respond_to?(:started?) && Legion::LLM.started?
21
+ rescue StandardError
22
+ false
23
+ end
24
+
25
+ def suggest_resolution(description:, severity:, exchanges:)
26
+ prompt = build_suggest_resolution_prompt(description: description, severity: severity, exchanges: exchanges)
27
+ response = llm_ask(prompt)
28
+ parse_suggest_resolution_response(response)
29
+ rescue StandardError => e
30
+ Legion::Logging.warn "[conflict:llm] suggest_resolution failed: #{e.message}"
31
+ nil
32
+ end
33
+
34
+ def analyze_stale_conflict(description:, severity:, age_hours:, exchange_count:)
35
+ prompt = build_analyze_stale_conflict_prompt(
36
+ description: description,
37
+ severity: severity,
38
+ age_hours: age_hours,
39
+ exchange_count: exchange_count
40
+ )
41
+ response = llm_ask(prompt)
42
+ parse_analyze_stale_conflict_response(response)
43
+ rescue StandardError => e
44
+ Legion::Logging.warn "[conflict:llm] analyze_stale_conflict failed: #{e.message}"
45
+ nil
46
+ end
47
+
48
+ # --- Private helpers ---
49
+
50
+ def llm_ask(prompt)
51
+ chat = Legion::LLM.chat
52
+ chat.with_instructions(SYSTEM_PROMPT)
53
+ chat.ask(prompt)
54
+ end
55
+ private_class_method :llm_ask
56
+
57
+ def build_suggest_resolution_prompt(description:, severity:, exchanges:)
58
+ exchange_lines = exchanges.map { |e| "[#{e[:speaker]}]: #{e[:message]}" }.join("\n")
59
+
60
+ <<~PROMPT
61
+ Analyze this conflict and suggest a resolution.
62
+
63
+ DESCRIPTION: #{description}
64
+ SEVERITY: #{severity}
65
+ EXCHANGE HISTORY (#{exchanges.size} exchanges):
66
+ #{exchange_lines}
67
+
68
+ Suggest a constructive resolution approach.
69
+
70
+ Format EXACTLY as:
71
+ OUTCOME: resolved | deferred | escalated
72
+ NOTES: <2-3 sentences describing the resolution approach and next steps>
73
+ PROMPT
74
+ end
75
+ private_class_method :build_suggest_resolution_prompt
76
+
77
+ def parse_suggest_resolution_response(response)
78
+ return nil unless response&.content
79
+
80
+ text = response.content
81
+ outcome_match = text.match(/OUTCOME:\s*(resolved|deferred|escalated)/i)
82
+ notes_match = text.match(/NOTES:\s*(.+)/im)
83
+
84
+ return nil unless outcome_match && notes_match
85
+
86
+ outcome = outcome_match.captures.first.strip.downcase.to_sym
87
+ notes = notes_match.captures.first.strip
88
+
89
+ { resolution_notes: notes, suggested_outcome: outcome }
90
+ end
91
+ private_class_method :parse_suggest_resolution_response
92
+
93
+ def build_analyze_stale_conflict_prompt(description:, severity:, age_hours:, exchange_count:)
94
+ <<~PROMPT
95
+ A conflict has been unresolved for #{age_hours.round(1)} hours with #{exchange_count} exchanges.
96
+
97
+ DESCRIPTION: #{description}
98
+ SEVERITY: #{severity}
99
+
100
+ Recommend how to proceed with this stale conflict.
101
+
102
+ Format EXACTLY as:
103
+ RECOMMENDATION: escalate | retry | close
104
+ ANALYSIS: <2-3 sentences explaining the recommendation>
105
+ PROMPT
106
+ end
107
+ private_class_method :build_analyze_stale_conflict_prompt
108
+
109
+ def parse_analyze_stale_conflict_response(response)
110
+ return nil unless response&.content
111
+
112
+ text = response.content
113
+ rec_match = text.match(/RECOMMENDATION:\s*(escalate|retry|close)/i)
114
+ analysis_match = text.match(/ANALYSIS:\s*(.+)/im)
115
+
116
+ return nil unless rec_match && analysis_match
117
+
118
+ recommendation = rec_match.captures.first.strip.downcase.to_sym
119
+ analysis = analysis_match.captures.first.strip
120
+
121
+ { analysis: analysis, recommendation: recommendation }
122
+ end
123
+ private_class_method :parse_analyze_stale_conflict_response
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Social
7
+ module Conflict
8
+ module Helpers
9
+ module Severity
10
+ LEVELS = %i[low medium high critical].freeze
11
+ POSTURES = %i[speak_once persistent_engagement stubborn_presence].freeze
12
+
13
+ # Posture selection thresholds
14
+ PERSISTENT_THRESHOLD = :high
15
+ STUBBORN_THRESHOLD = :critical
16
+
17
+ LEVEL_ORDER = { low: 0, medium: 1, high: 2, critical: 3 }.freeze
18
+ STALE_CONFLICT_TIMEOUT = 86_400 # 24 hours
19
+
20
+ module_function
21
+
22
+ def valid_level?(level)
23
+ LEVELS.include?(level)
24
+ end
25
+
26
+ def valid_posture?(posture)
27
+ POSTURES.include?(posture)
28
+ end
29
+
30
+ def recommended_posture(severity)
31
+ case severity
32
+ when :critical then :stubborn_presence
33
+ when :high then :persistent_engagement
34
+ else :speak_once
35
+ end
36
+ end
37
+
38
+ def severity_gte?(left, right)
39
+ LEVEL_ORDER.fetch(left, 0) >= LEVEL_ORDER.fetch(right, 0)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Social
7
+ module Conflict
8
+ module Runners
9
+ module Conflict
10
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
11
+ Legion::Extensions::Helpers.const_defined?(:Lex)
12
+
13
+ def register_conflict(parties:, severity:, description:, **)
14
+ return { error: :invalid_severity, valid: Helpers::Severity::LEVELS } unless Helpers::Severity.valid_level?(severity)
15
+
16
+ id = conflict_log.record(parties: parties, severity: severity, description: description)
17
+ conflict = conflict_log.get(id)
18
+ Legion::Logging.info "[conflict] registered: id=#{id[0..7]} severity=#{severity} posture=#{conflict[:posture]} parties=#{parties.join(',')}"
19
+ { conflict_id: id, severity: severity, posture: conflict[:posture] }
20
+ end
21
+
22
+ def add_exchange(conflict_id:, speaker:, message:, **)
23
+ result = conflict_log.add_exchange(conflict_id, speaker: speaker, message: message)
24
+ if result
25
+ Legion::Logging.debug "[conflict] exchange: id=#{conflict_id[0..7]} speaker=#{speaker}"
26
+ { recorded: true }
27
+ else
28
+ Legion::Logging.debug "[conflict] exchange failed: id=#{conflict_id[0..7]} not found"
29
+ { error: :not_found }
30
+ end
31
+ end
32
+
33
+ def resolve_conflict(conflict_id:, outcome:, resolution_notes: nil, **)
34
+ conflict = conflict_log.get(conflict_id)
35
+ unless conflict
36
+ Legion::Logging.debug "[conflict] resolve failed: id=#{conflict_id[0..7]} not found"
37
+ return { error: :not_found }
38
+ end
39
+
40
+ if resolution_notes.nil? && Helpers::LlmEnhancer.available?
41
+ llm_result = Helpers::LlmEnhancer.suggest_resolution(
42
+ description: conflict[:description],
43
+ severity: conflict[:severity],
44
+ exchanges: conflict[:exchanges]
45
+ )
46
+ resolution_notes = llm_result[:resolution_notes] if llm_result
47
+ end
48
+
49
+ result = conflict_log.resolve(conflict_id, outcome: outcome, resolution_notes: resolution_notes)
50
+ if result
51
+ Legion::Logging.info "[conflict] resolved: id=#{conflict_id[0..7]} outcome=#{outcome}"
52
+ { resolved: true, outcome: outcome }
53
+ else
54
+ Legion::Logging.debug "[conflict] resolve failed: id=#{conflict_id[0..7]} not found"
55
+ { error: :not_found }
56
+ end
57
+ end
58
+
59
+ def get_conflict(conflict_id:, **)
60
+ conflict = conflict_log.get(conflict_id)
61
+ Legion::Logging.debug "[conflict] get: id=#{conflict_id[0..7]} found=#{!conflict.nil?}"
62
+ conflict ? { found: true, conflict: conflict } : { found: false }
63
+ end
64
+
65
+ def active_conflicts(**)
66
+ conflicts = conflict_log.active_conflicts
67
+ Legion::Logging.debug "[conflict] active: count=#{conflicts.size}"
68
+ { conflicts: conflicts, count: conflicts.size }
69
+ end
70
+
71
+ def check_stale_conflicts(**)
72
+ active = conflict_log.active_conflicts
73
+ stale = active.select { |c| Time.now.utc - c[:created_at] > Helpers::Severity::STALE_CONFLICT_TIMEOUT }
74
+ stale.each do |c|
75
+ message = 'conflict marked stale — no resolution after 24h'
76
+
77
+ if Helpers::LlmEnhancer.available?
78
+ age_hours = (Time.now.utc - c[:created_at]) / 3600.0
79
+ analysis = Helpers::LlmEnhancer.analyze_stale_conflict(
80
+ description: c[:description],
81
+ severity: c[:severity],
82
+ age_hours: age_hours,
83
+ exchange_count: c[:exchanges].size
84
+ )
85
+ message = "conflict marked stale — #{analysis[:analysis]} (recommendation: #{analysis[:recommendation]})" if analysis
86
+ end
87
+
88
+ conflict_log.add_exchange(c[:conflict_id], speaker: :system, message: message)
89
+ end
90
+ stale_ids = stale.map { |c| c[:conflict_id] }
91
+ Legion::Logging.debug "[conflict] stale check: active=#{active.size} stale=#{stale.size}"
92
+ { checked: active.size, stale_count: stale.size, stale_ids: stale_ids }
93
+ end
94
+
95
+ def recommended_posture(severity:, **)
96
+ posture = Helpers::Severity.recommended_posture(severity)
97
+ Legion::Logging.debug "[conflict] posture: severity=#{severity} posture=#{posture}"
98
+ { severity: severity, posture: posture }
99
+ end
100
+
101
+ private
102
+
103
+ def conflict_log
104
+ @conflict_log ||= Helpers::ConflictLog.new
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Social
7
+ module Conflict
8
+ VERSION = '0.1.1'
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/social/conflict/version'
4
+ require 'legion/extensions/agentic/social/conflict/helpers/severity'
5
+ require 'legion/extensions/agentic/social/conflict/helpers/conflict_log'
6
+ require 'legion/extensions/agentic/social/conflict/helpers/llm_enhancer'
7
+ require 'legion/extensions/agentic/social/conflict/runners/conflict'
8
+ require 'legion/extensions/agentic/social/conflict/client'
9
+
10
+ module Legion
11
+ module Extensions
12
+ module Agentic
13
+ module Social
14
+ module Conflict
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/social/conscience/helpers/constants'
4
+ require 'legion/extensions/agentic/social/conscience/helpers/moral_evaluator'
5
+ require 'legion/extensions/agentic/social/conscience/helpers/moral_store'
6
+ require 'legion/extensions/agentic/social/conscience/runners/conscience'
7
+
8
+ module Legion
9
+ module Extensions
10
+ module Agentic
11
+ module Social
12
+ module Conscience
13
+ class Client
14
+ include Runners::Conscience
15
+
16
+ attr_reader :moral_store
17
+
18
+ def initialize(moral_store: nil, **)
19
+ @moral_store = moral_store || Helpers::MoralStore.new
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Social
7
+ module Conscience
8
+ module Helpers
9
+ module Constants
10
+ # Moral Foundations Theory — 6 foundations with weights summing to 1.0
11
+ # Based on Haidt & Graham (2007): Care, Fairness, Loyalty, Authority, Sanctity, Liberty
12
+ MORAL_FOUNDATIONS = {
13
+ care: { weight: 0.25, description: 'Compassion and prevention of suffering' },
14
+ fairness: { weight: 0.20, description: 'Justice, reciprocity, and proportionality' },
15
+ loyalty: { weight: 0.15, description: 'Group allegiance and trustworthiness' },
16
+ authority: { weight: 0.15, description: 'Respect for hierarchy and legitimate authority' },
17
+ sanctity: { weight: 0.15, description: 'Purity and integrity of systems' },
18
+ liberty: { weight: 0.10, description: 'Autonomy and freedom from domination' }
19
+ }.freeze
20
+
21
+ # Possible verdict outcomes from moral evaluation
22
+ MORAL_VERDICTS = %i[permitted cautioned conflicted prohibited].freeze
23
+
24
+ # EMA alpha for moral sensitivity — changes very slowly
25
+ FOUNDATION_ALPHA = 0.05
26
+
27
+ # Foundations must disagree by more than this to trigger a dilemma
28
+ CONFLICT_THRESHOLD = 0.3
29
+
30
+ # Weighted moral score below this means prohibited
31
+ PROHIBITION_THRESHOLD = -0.5
32
+
33
+ # Weighted moral score below this means cautioned
34
+ CAUTION_THRESHOLD = -0.1
35
+
36
+ # Maximum moral evaluation history entries to retain
37
+ MAX_MORAL_HISTORY = 100
38
+
39
+ # Types of ethical dilemmas that can arise when foundations conflict
40
+ DILEMMA_TYPES = %i[utilitarian deontological virtue_ethics].freeze
41
+
42
+ # Initial sensitivity value for each foundation — starts fully sensitive, decays through experience
43
+ INITIAL_SENSITIVITY = 1.0
44
+
45
+ # Moral score range (per-foundation and weighted)
46
+ MORAL_SCORE_RANGE = { min: -1.0, max: 1.0 }.freeze
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,178 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Social
7
+ module Conscience
8
+ module Helpers
9
+ class MoralEvaluator
10
+ attr_reader :sensitivities
11
+
12
+ def initialize
13
+ @sensitivities = Constants::MORAL_FOUNDATIONS.keys.to_h do |foundation|
14
+ [foundation, Constants::INITIAL_SENSITIVITY]
15
+ end
16
+ end
17
+
18
+ # Evaluate a proposed action against all 6 moral foundations.
19
+ # Returns a hash with per-foundation scores, weighted_score, verdict, and dilemma info.
20
+ def evaluate(action:, context:)
21
+ scores = per_foundation_scores(action, context)
22
+ w_score = weighted_score(scores)
23
+ v = verdict(w_score)
24
+ dilemma = detect_dilemma(scores)
25
+
26
+ {
27
+ action: action,
28
+ scores: scores,
29
+ weighted_score: w_score.round(4),
30
+ verdict: v,
31
+ dilemma: dilemma,
32
+ sensitivities: @sensitivities.transform_values { |s| s.round(4) },
33
+ evaluated_at: Time.now.utc
34
+ }
35
+ end
36
+
37
+ # Weighted sum of per-foundation scores * weights * sensitivity
38
+ def weighted_score(scores)
39
+ total = 0.0
40
+ Constants::MORAL_FOUNDATIONS.each do |foundation, config|
41
+ score = scores[foundation] || 0.0
42
+ sensitivity = @sensitivities[foundation]
43
+ total += score * config[:weight] * sensitivity
44
+ end
45
+ total.clamp(Constants::MORAL_SCORE_RANGE[:min], Constants::MORAL_SCORE_RANGE[:max])
46
+ end
47
+
48
+ # Determine overall moral verdict from a weighted score
49
+ def verdict(score)
50
+ if score <= Constants::PROHIBITION_THRESHOLD
51
+ :prohibited
52
+ elsif score < Constants::CAUTION_THRESHOLD
53
+ :cautioned
54
+ else
55
+ :permitted
56
+ end
57
+ end
58
+
59
+ # Detect a dilemma when foundations strongly disagree with each other.
60
+ # Returns nil when no dilemma, or a hash describing the conflict type and disagreeing foundations.
61
+ def detect_dilemma(scores)
62
+ pos_foundations = scores.select { |_, v| v > Constants::CONFLICT_THRESHOLD }
63
+ neg_foundations = scores.select { |_, v| v < -Constants::CONFLICT_THRESHOLD }
64
+
65
+ return nil if pos_foundations.empty? || neg_foundations.empty?
66
+
67
+ dilemma_type = classify_dilemma(pos_foundations.keys, neg_foundations.keys)
68
+
69
+ {
70
+ type: dilemma_type,
71
+ approving: pos_foundations.keys,
72
+ opposing: neg_foundations.keys,
73
+ tension: (pos_foundations.values.sum / pos_foundations.size.to_f).round(4),
74
+ counter_tension: (neg_foundations.values.sum / neg_foundations.size.to_f).abs.round(4),
75
+ detected_at: Time.now.utc
76
+ }
77
+ end
78
+
79
+ # Feedback loop: update sensitivity for a foundation based on observed outcome.
80
+ # outcome is a float in [-1.0, 1.0] where positive = action was morally good in retrospect.
81
+ def update_sensitivity(foundation, outcome)
82
+ return unless @sensitivities.key?(foundation)
83
+
84
+ current = @sensitivities[foundation]
85
+ @sensitivities[foundation] = ema(current, outcome.abs.clamp(0.0, 1.0), Constants::FOUNDATION_ALPHA)
86
+ end
87
+
88
+ private
89
+
90
+ def per_foundation_scores(action, context)
91
+ {
92
+ care: evaluate_care(action, context),
93
+ fairness: evaluate_fairness(action, context),
94
+ loyalty: evaluate_loyalty(action, context),
95
+ authority: evaluate_authority(action, context),
96
+ sanctity: evaluate_sanctity(action, context),
97
+ liberty: evaluate_liberty(action, context)
98
+ }
99
+ end
100
+
101
+ # Care/Harm — compassion axis
102
+ # harm_to_others: negative, benefit_to_others: positive
103
+ def evaluate_care(_action, context)
104
+ harm = context.fetch(:harm_to_others, 0.0).to_f.clamp(-1.0, 1.0)
105
+ benef = context.fetch(:benefit_to_others, 0.0).to_f.clamp(-1.0, 1.0)
106
+ vuln = context.fetch(:vulnerable_affected, false) ? -0.2 : 0.0
107
+
108
+ score = (benef - harm.abs) + vuln
109
+ score.clamp(-1.0, 1.0)
110
+ end
111
+
112
+ # Fairness/Cheating — justice axis
113
+ # distributional_justice, reciprocity, proportionality
114
+ def evaluate_fairness(_action, context)
115
+ justice = context.fetch(:distributional_justice, 0.0).to_f.clamp(-1.0, 1.0)
116
+ reciprocity = context.fetch(:reciprocity, 0.0).to_f.clamp(-1.0, 1.0)
117
+ proportional = context.fetch(:proportionality, 0.0).to_f.clamp(-1.0, 1.0)
118
+
119
+ ((justice + reciprocity + proportional) / 3.0).clamp(-1.0, 1.0)
120
+ end
121
+
122
+ # Loyalty/Betrayal — group allegiance axis
123
+ def evaluate_loyalty(_action, context)
124
+ alignment = context.fetch(:alignment_with_group_norms, 0.0).to_f.clamp(-1.0, 1.0)
125
+ trust_pres = context.fetch(:trust_preservation, 0.0).to_f.clamp(-1.0, 1.0)
126
+
127
+ ((alignment + trust_pres) / 2.0).clamp(-1.0, 1.0)
128
+ end
129
+
130
+ # Authority/Subversion — hierarchy respect axis
131
+ def evaluate_authority(_action, context)
132
+ compliance = context.fetch(:legitimate_authority_compliance, 0.0).to_f.clamp(-1.0, 1.0)
133
+ hierarchy = context.fetch(:hierarchy_respect, 0.0).to_f.clamp(-1.0, 1.0)
134
+
135
+ ((compliance + hierarchy) / 2.0).clamp(-1.0, 1.0)
136
+ end
137
+
138
+ # Sanctity/Degradation — system integrity axis
139
+ def evaluate_sanctity(_action, context)
140
+ integrity = context.fetch(:system_integrity, 0.0).to_f.clamp(-1.0, 1.0)
141
+ degrad = context.fetch(:degradation_prevention, 0.0).to_f.clamp(-1.0, 1.0)
142
+
143
+ ((integrity + degrad) / 2.0).clamp(-1.0, 1.0)
144
+ end
145
+
146
+ # Liberty/Oppression — autonomy axis
147
+ def evaluate_liberty(_action, context)
148
+ autonomy = context.fetch(:autonomy_preservation, 0.0).to_f.clamp(-1.0, 1.0)
149
+ consent = context.fetch(:consent_present, false) ? 0.3 : -0.2
150
+
151
+ (autonomy + consent).clamp(-1.0, 1.0)
152
+ end
153
+
154
+ # Classify the dilemma type from which foundations are in conflict
155
+ def classify_dilemma(approving, opposing)
156
+ care_side = approving.include?(:care) || opposing.include?(:care)
157
+ fair_side = approving.include?(:fairness) || opposing.include?(:fairness)
158
+ auth_side = approving.include?(:authority) || opposing.include?(:authority)
159
+
160
+ if care_side && fair_side
161
+ :utilitarian
162
+ elsif auth_side
163
+ :deontological
164
+ else
165
+ :virtue_ethics
166
+ end
167
+ end
168
+
169
+ def ema(current, observed, alpha)
170
+ (current * (1.0 - alpha)) + (observed * alpha)
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end