lex-agentic-defense 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 (219) 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-defense.gemspec +30 -0
  7. data/lib/legion/extensions/agentic/defense/avalanche/client.rb +22 -0
  8. data/lib/legion/extensions/agentic/defense/avalanche/helpers/avalanche_engine.rb +132 -0
  9. data/lib/legion/extensions/agentic/defense/avalanche/helpers/cascade.rb +76 -0
  10. data/lib/legion/extensions/agentic/defense/avalanche/helpers/constants.rb +44 -0
  11. data/lib/legion/extensions/agentic/defense/avalanche/helpers/snowpack.rb +86 -0
  12. data/lib/legion/extensions/agentic/defense/avalanche/runners/cognitive_avalanche.rb +75 -0
  13. data/lib/legion/extensions/agentic/defense/avalanche/version.rb +13 -0
  14. data/lib/legion/extensions/agentic/defense/avalanche.rb +22 -0
  15. data/lib/legion/extensions/agentic/defense/bias/actors/update.rb +45 -0
  16. data/lib/legion/extensions/agentic/defense/bias/client.rb +30 -0
  17. data/lib/legion/extensions/agentic/defense/bias/helpers/bias_detector.rb +107 -0
  18. data/lib/legion/extensions/agentic/defense/bias/helpers/bias_event.rb +44 -0
  19. data/lib/legion/extensions/agentic/defense/bias/helpers/bias_store.rb +84 -0
  20. data/lib/legion/extensions/agentic/defense/bias/helpers/constants.rb +28 -0
  21. data/lib/legion/extensions/agentic/defense/bias/runners/bias.rb +151 -0
  22. data/lib/legion/extensions/agentic/defense/bias/version.rb +13 -0
  23. data/lib/legion/extensions/agentic/defense/bias.rb +20 -0
  24. data/lib/legion/extensions/agentic/defense/confabulation/actors/decay.rb +45 -0
  25. data/lib/legion/extensions/agentic/defense/confabulation/client.rb +28 -0
  26. data/lib/legion/extensions/agentic/defense/confabulation/helpers/claim.rb +67 -0
  27. data/lib/legion/extensions/agentic/defense/confabulation/helpers/confabulation_engine.rb +120 -0
  28. data/lib/legion/extensions/agentic/defense/confabulation/helpers/constants.rb +29 -0
  29. data/lib/legion/extensions/agentic/defense/confabulation/runners/confabulation.rb +74 -0
  30. data/lib/legion/extensions/agentic/defense/confabulation/version.rb +13 -0
  31. data/lib/legion/extensions/agentic/defense/confabulation.rb +19 -0
  32. data/lib/legion/extensions/agentic/defense/dissonance/client.rb +32 -0
  33. data/lib/legion/extensions/agentic/defense/dissonance/helpers/belief.rb +46 -0
  34. data/lib/legion/extensions/agentic/defense/dissonance/helpers/constants.rb +27 -0
  35. data/lib/legion/extensions/agentic/defense/dissonance/helpers/dissonance_event.rb +52 -0
  36. data/lib/legion/extensions/agentic/defense/dissonance/helpers/dissonance_model.rb +159 -0
  37. data/lib/legion/extensions/agentic/defense/dissonance/runners/dissonance.rb +163 -0
  38. data/lib/legion/extensions/agentic/defense/dissonance/version.rb +13 -0
  39. data/lib/legion/extensions/agentic/defense/dissonance.rb +20 -0
  40. data/lib/legion/extensions/agentic/defense/epistemic_vigilance/actors/update.rb +45 -0
  41. data/lib/legion/extensions/agentic/defense/epistemic_vigilance/client.rb +27 -0
  42. data/lib/legion/extensions/agentic/defense/epistemic_vigilance/helpers/claim.rb +78 -0
  43. data/lib/legion/extensions/agentic/defense/epistemic_vigilance/helpers/client.rb +23 -0
  44. data/lib/legion/extensions/agentic/defense/epistemic_vigilance/helpers/constants.rb +37 -0
  45. data/lib/legion/extensions/agentic/defense/epistemic_vigilance/helpers/source.rb +64 -0
  46. data/lib/legion/extensions/agentic/defense/epistemic_vigilance/helpers/vigilance_engine.rb +195 -0
  47. data/lib/legion/extensions/agentic/defense/epistemic_vigilance/runners/epistemic_vigilance.rb +91 -0
  48. data/lib/legion/extensions/agentic/defense/epistemic_vigilance/version.rb +13 -0
  49. data/lib/legion/extensions/agentic/defense/epistemic_vigilance.rb +20 -0
  50. data/lib/legion/extensions/agentic/defense/erosion/client.rb +23 -0
  51. data/lib/legion/extensions/agentic/defense/erosion/helpers/channel.rb +84 -0
  52. data/lib/legion/extensions/agentic/defense/erosion/helpers/constants.rb +47 -0
  53. data/lib/legion/extensions/agentic/defense/erosion/helpers/erosion_engine.rb +134 -0
  54. data/lib/legion/extensions/agentic/defense/erosion/helpers/formation.rb +100 -0
  55. data/lib/legion/extensions/agentic/defense/erosion/runners/cognitive_erosion.rb +93 -0
  56. data/lib/legion/extensions/agentic/defense/erosion/version.rb +13 -0
  57. data/lib/legion/extensions/agentic/defense/erosion.rb +21 -0
  58. data/lib/legion/extensions/agentic/defense/error_monitoring/actors/tick.rb +45 -0
  59. data/lib/legion/extensions/agentic/defense/error_monitoring/client.rb +28 -0
  60. data/lib/legion/extensions/agentic/defense/error_monitoring/helpers/constants.rb +50 -0
  61. data/lib/legion/extensions/agentic/defense/error_monitoring/helpers/error_monitor.rb +174 -0
  62. data/lib/legion/extensions/agentic/defense/error_monitoring/helpers/error_signal.rb +60 -0
  63. data/lib/legion/extensions/agentic/defense/error_monitoring/runners/error_monitoring.rb +102 -0
  64. data/lib/legion/extensions/agentic/defense/error_monitoring/version.rb +13 -0
  65. data/lib/legion/extensions/agentic/defense/error_monitoring.rb +19 -0
  66. data/lib/legion/extensions/agentic/defense/extinction/actors/protocol_monitor.rb +45 -0
  67. data/lib/legion/extensions/agentic/defense/extinction/client.rb +27 -0
  68. data/lib/legion/extensions/agentic/defense/extinction/helpers/levels.rb +43 -0
  69. data/lib/legion/extensions/agentic/defense/extinction/helpers/protocol_state.rb +125 -0
  70. data/lib/legion/extensions/agentic/defense/extinction/local_migrations/20260316000040_create_extinction_state.rb +13 -0
  71. data/lib/legion/extensions/agentic/defense/extinction/runners/extinction.rb +130 -0
  72. data/lib/legion/extensions/agentic/defense/extinction/version.rb +13 -0
  73. data/lib/legion/extensions/agentic/defense/extinction.rb +25 -0
  74. data/lib/legion/extensions/agentic/defense/friction/client.rb +15 -0
  75. data/lib/legion/extensions/agentic/defense/friction/helpers/constants.rb +38 -0
  76. data/lib/legion/extensions/agentic/defense/friction/helpers/friction_engine.rb +131 -0
  77. data/lib/legion/extensions/agentic/defense/friction/helpers/state_transition.rb +73 -0
  78. data/lib/legion/extensions/agentic/defense/friction/runners/cognitive_friction.rb +82 -0
  79. data/lib/legion/extensions/agentic/defense/friction/version.rb +13 -0
  80. data/lib/legion/extensions/agentic/defense/friction.rb +19 -0
  81. data/lib/legion/extensions/agentic/defense/immune_response/client.rb +19 -0
  82. data/lib/legion/extensions/agentic/defense/immune_response/helpers/antibody.rb +72 -0
  83. data/lib/legion/extensions/agentic/defense/immune_response/helpers/antigen.rb +87 -0
  84. data/lib/legion/extensions/agentic/defense/immune_response/helpers/constants.rb +75 -0
  85. data/lib/legion/extensions/agentic/defense/immune_response/helpers/immune_engine.rb +184 -0
  86. data/lib/legion/extensions/agentic/defense/immune_response/helpers/immune_response.rb +76 -0
  87. data/lib/legion/extensions/agentic/defense/immune_response/runners/cognitive_immune_response.rb +114 -0
  88. data/lib/legion/extensions/agentic/defense/immune_response/version.rb +13 -0
  89. data/lib/legion/extensions/agentic/defense/immune_response.rb +21 -0
  90. data/lib/legion/extensions/agentic/defense/immunology/client.rb +29 -0
  91. data/lib/legion/extensions/agentic/defense/immunology/helpers/antibody.rb +55 -0
  92. data/lib/legion/extensions/agentic/defense/immunology/helpers/constants.rb +43 -0
  93. data/lib/legion/extensions/agentic/defense/immunology/helpers/immune_engine.rb +187 -0
  94. data/lib/legion/extensions/agentic/defense/immunology/helpers/threat.rb +67 -0
  95. data/lib/legion/extensions/agentic/defense/immunology/runners/cognitive_immunology.rb +92 -0
  96. data/lib/legion/extensions/agentic/defense/immunology/version.rb +13 -0
  97. data/lib/legion/extensions/agentic/defense/immunology.rb +20 -0
  98. data/lib/legion/extensions/agentic/defense/phantom/client.rb +29 -0
  99. data/lib/legion/extensions/agentic/defense/phantom/helpers/constants.rb +54 -0
  100. data/lib/legion/extensions/agentic/defense/phantom/helpers/phantom_engine.rb +106 -0
  101. data/lib/legion/extensions/agentic/defense/phantom/helpers/phantom_limb.rb +103 -0
  102. data/lib/legion/extensions/agentic/defense/phantom/helpers/phantom_signal.rb +40 -0
  103. data/lib/legion/extensions/agentic/defense/phantom/runners/cognitive_phantom.rb +79 -0
  104. data/lib/legion/extensions/agentic/defense/phantom/version.rb +13 -0
  105. data/lib/legion/extensions/agentic/defense/phantom.rb +21 -0
  106. data/lib/legion/extensions/agentic/defense/quicksand/client.rb +15 -0
  107. data/lib/legion/extensions/agentic/defense/quicksand/helpers/constants.rb +48 -0
  108. data/lib/legion/extensions/agentic/defense/quicksand/helpers/pit.rb +82 -0
  109. data/lib/legion/extensions/agentic/defense/quicksand/helpers/quicksand_engine.rb +137 -0
  110. data/lib/legion/extensions/agentic/defense/quicksand/helpers/trap.rb +101 -0
  111. data/lib/legion/extensions/agentic/defense/quicksand/runners/cognitive_quicksand.rb +84 -0
  112. data/lib/legion/extensions/agentic/defense/quicksand/version.rb +13 -0
  113. data/lib/legion/extensions/agentic/defense/quicksand.rb +22 -0
  114. data/lib/legion/extensions/agentic/defense/quicksilver/client.rb +29 -0
  115. data/lib/legion/extensions/agentic/defense/quicksilver/helpers/constants.rb +50 -0
  116. data/lib/legion/extensions/agentic/defense/quicksilver/helpers/droplet.rb +126 -0
  117. data/lib/legion/extensions/agentic/defense/quicksilver/helpers/pool.rb +83 -0
  118. data/lib/legion/extensions/agentic/defense/quicksilver/helpers/quicksilver_engine.rb +124 -0
  119. data/lib/legion/extensions/agentic/defense/quicksilver/runners/cognitive_quicksilver.rb +130 -0
  120. data/lib/legion/extensions/agentic/defense/quicksilver/version.rb +13 -0
  121. data/lib/legion/extensions/agentic/defense/quicksilver.rb +21 -0
  122. data/lib/legion/extensions/agentic/defense/version.rb +11 -0
  123. data/lib/legion/extensions/agentic/defense/whirlpool/client.rb +65 -0
  124. data/lib/legion/extensions/agentic/defense/whirlpool/helpers/captured_thought.rb +67 -0
  125. data/lib/legion/extensions/agentic/defense/whirlpool/helpers/constants.rb +45 -0
  126. data/lib/legion/extensions/agentic/defense/whirlpool/helpers/vortex.rb +91 -0
  127. data/lib/legion/extensions/agentic/defense/whirlpool/helpers/whirlpool_engine.rb +92 -0
  128. data/lib/legion/extensions/agentic/defense/whirlpool/runners/cognitive_whirlpool.rb +117 -0
  129. data/lib/legion/extensions/agentic/defense/whirlpool/version.rb +13 -0
  130. data/lib/legion/extensions/agentic/defense/whirlpool.rb +22 -0
  131. data/lib/legion/extensions/agentic/defense.rb +32 -0
  132. data/spec/legion/extensions/agentic/defense/avalanche/client_spec.rb +96 -0
  133. data/spec/legion/extensions/agentic/defense/avalanche/helpers/avalanche_engine_spec.rb +276 -0
  134. data/spec/legion/extensions/agentic/defense/avalanche/helpers/cascade_spec.rb +190 -0
  135. data/spec/legion/extensions/agentic/defense/avalanche/helpers/constants_spec.rb +129 -0
  136. data/spec/legion/extensions/agentic/defense/avalanche/helpers/snowpack_spec.rb +197 -0
  137. data/spec/legion/extensions/agentic/defense/avalanche/runners/cognitive_avalanche_spec.rb +211 -0
  138. data/spec/legion/extensions/agentic/defense/bias/client_spec.rb +16 -0
  139. data/spec/legion/extensions/agentic/defense/bias/helpers/bias_detector_spec.rb +160 -0
  140. data/spec/legion/extensions/agentic/defense/bias/helpers/bias_event_spec.rb +64 -0
  141. data/spec/legion/extensions/agentic/defense/bias/helpers/bias_store_spec.rb +143 -0
  142. data/spec/legion/extensions/agentic/defense/bias/runners/bias_spec.rb +155 -0
  143. data/spec/legion/extensions/agentic/defense/confabulation/client_spec.rb +34 -0
  144. data/spec/legion/extensions/agentic/defense/confabulation/helpers/claim_spec.rb +119 -0
  145. data/spec/legion/extensions/agentic/defense/confabulation/helpers/confabulation_engine_spec.rb +163 -0
  146. data/spec/legion/extensions/agentic/defense/confabulation/helpers/constants_spec.rb +55 -0
  147. data/spec/legion/extensions/agentic/defense/confabulation/runners/confabulation_spec.rb +119 -0
  148. data/spec/legion/extensions/agentic/defense/dissonance/client_spec.rb +51 -0
  149. data/spec/legion/extensions/agentic/defense/dissonance/helpers/belief_spec.rb +103 -0
  150. data/spec/legion/extensions/agentic/defense/dissonance/helpers/constants_spec.rb +60 -0
  151. data/spec/legion/extensions/agentic/defense/dissonance/helpers/dissonance_event_spec.rb +113 -0
  152. data/spec/legion/extensions/agentic/defense/dissonance/helpers/dissonance_model_spec.rb +252 -0
  153. data/spec/legion/extensions/agentic/defense/dissonance/runners/dissonance_spec.rb +323 -0
  154. data/spec/legion/extensions/agentic/defense/epistemic_vigilance/client_spec.rb +28 -0
  155. data/spec/legion/extensions/agentic/defense/epistemic_vigilance/helpers/claim_spec.rb +135 -0
  156. data/spec/legion/extensions/agentic/defense/epistemic_vigilance/helpers/constants_spec.rb +59 -0
  157. data/spec/legion/extensions/agentic/defense/epistemic_vigilance/helpers/source_spec.rb +117 -0
  158. data/spec/legion/extensions/agentic/defense/epistemic_vigilance/helpers/vigilance_engine_spec.rb +273 -0
  159. data/spec/legion/extensions/agentic/defense/epistemic_vigilance/runners/epistemic_vigilance_spec.rb +157 -0
  160. data/spec/legion/extensions/agentic/defense/erosion/client_spec.rb +90 -0
  161. data/spec/legion/extensions/agentic/defense/erosion/helpers/channel_spec.rb +173 -0
  162. data/spec/legion/extensions/agentic/defense/erosion/helpers/constants_spec.rb +137 -0
  163. data/spec/legion/extensions/agentic/defense/erosion/helpers/erosion_engine_spec.rb +263 -0
  164. data/spec/legion/extensions/agentic/defense/erosion/helpers/formation_spec.rb +206 -0
  165. data/spec/legion/extensions/agentic/defense/erosion/runners/cognitive_erosion_spec.rb +153 -0
  166. data/spec/legion/extensions/agentic/defense/error_monitoring/client_spec.rb +40 -0
  167. data/spec/legion/extensions/agentic/defense/error_monitoring/helpers/error_monitor_spec.rb +178 -0
  168. data/spec/legion/extensions/agentic/defense/error_monitoring/helpers/error_signal_spec.rb +76 -0
  169. data/spec/legion/extensions/agentic/defense/error_monitoring/runners/error_monitoring_spec.rb +87 -0
  170. data/spec/legion/extensions/agentic/defense/extinction/actors/protocol_monitor_spec.rb +45 -0
  171. data/spec/legion/extensions/agentic/defense/extinction/client_spec.rb +13 -0
  172. data/spec/legion/extensions/agentic/defense/extinction/helpers/levels_spec.rb +180 -0
  173. data/spec/legion/extensions/agentic/defense/extinction/helpers/protocol_state_spec.rb +291 -0
  174. data/spec/legion/extensions/agentic/defense/extinction/local_persistence_spec.rb +188 -0
  175. data/spec/legion/extensions/agentic/defense/extinction/runners/extinction_spec.rb +114 -0
  176. data/spec/legion/extensions/agentic/defense/friction/helpers/constants_spec.rb +46 -0
  177. data/spec/legion/extensions/agentic/defense/friction/helpers/friction_engine_spec.rb +175 -0
  178. data/spec/legion/extensions/agentic/defense/friction/helpers/state_transition_spec.rb +124 -0
  179. data/spec/legion/extensions/agentic/defense/friction/runners/cognitive_friction_spec.rb +89 -0
  180. data/spec/legion/extensions/agentic/defense/immune_response/client_spec.rb +32 -0
  181. data/spec/legion/extensions/agentic/defense/immune_response/cognitive_immune_response_spec.rb +7 -0
  182. data/spec/legion/extensions/agentic/defense/immune_response/helpers/antibody_spec.rb +117 -0
  183. data/spec/legion/extensions/agentic/defense/immune_response/helpers/antigen_spec.rb +125 -0
  184. data/spec/legion/extensions/agentic/defense/immune_response/helpers/constants_spec.rb +45 -0
  185. data/spec/legion/extensions/agentic/defense/immune_response/helpers/immune_engine_spec.rb +222 -0
  186. data/spec/legion/extensions/agentic/defense/immune_response/helpers/immune_response_spec.rb +84 -0
  187. data/spec/legion/extensions/agentic/defense/immune_response/runners_spec.rb +141 -0
  188. data/spec/legion/extensions/agentic/defense/immunology/client_spec.rb +61 -0
  189. data/spec/legion/extensions/agentic/defense/immunology/helpers/antibody_spec.rb +98 -0
  190. data/spec/legion/extensions/agentic/defense/immunology/helpers/constants_spec.rb +86 -0
  191. data/spec/legion/extensions/agentic/defense/immunology/helpers/immune_engine_spec.rb +275 -0
  192. data/spec/legion/extensions/agentic/defense/immunology/helpers/threat_spec.rb +133 -0
  193. data/spec/legion/extensions/agentic/defense/immunology/runners/cognitive_immunology_spec.rb +177 -0
  194. data/spec/legion/extensions/agentic/defense/phantom/client_spec.rb +53 -0
  195. data/spec/legion/extensions/agentic/defense/phantom/helpers/constants_spec.rb +87 -0
  196. data/spec/legion/extensions/agentic/defense/phantom/helpers/phantom_engine_spec.rb +222 -0
  197. data/spec/legion/extensions/agentic/defense/phantom/helpers/phantom_limb_spec.rb +180 -0
  198. data/spec/legion/extensions/agentic/defense/phantom/helpers/phantom_signal_spec.rb +59 -0
  199. data/spec/legion/extensions/agentic/defense/phantom/runners/cognitive_phantom_spec.rb +193 -0
  200. data/spec/legion/extensions/agentic/defense/quicksand/client_spec.rb +35 -0
  201. data/spec/legion/extensions/agentic/defense/quicksand/helpers/constants_spec.rb +58 -0
  202. data/spec/legion/extensions/agentic/defense/quicksand/helpers/pit_spec.rb +103 -0
  203. data/spec/legion/extensions/agentic/defense/quicksand/helpers/quicksand_engine_spec.rb +153 -0
  204. data/spec/legion/extensions/agentic/defense/quicksand/helpers/trap_spec.rb +166 -0
  205. data/spec/legion/extensions/agentic/defense/quicksand/runners/cognitive_quicksand_spec.rb +90 -0
  206. data/spec/legion/extensions/agentic/defense/quicksilver/client_spec.rb +72 -0
  207. data/spec/legion/extensions/agentic/defense/quicksilver/helpers/constants_spec.rb +105 -0
  208. data/spec/legion/extensions/agentic/defense/quicksilver/helpers/droplet_spec.rb +310 -0
  209. data/spec/legion/extensions/agentic/defense/quicksilver/helpers/pool_spec.rb +174 -0
  210. data/spec/legion/extensions/agentic/defense/quicksilver/helpers/quicksilver_engine_spec.rb +226 -0
  211. data/spec/legion/extensions/agentic/defense/quicksilver/runners/cognitive_quicksilver_spec.rb +227 -0
  212. data/spec/legion/extensions/agentic/defense/whirlpool/client_spec.rb +63 -0
  213. data/spec/legion/extensions/agentic/defense/whirlpool/helpers/captured_thought_spec.rb +171 -0
  214. data/spec/legion/extensions/agentic/defense/whirlpool/helpers/constants_spec.rb +65 -0
  215. data/spec/legion/extensions/agentic/defense/whirlpool/helpers/vortex_spec.rb +189 -0
  216. data/spec/legion/extensions/agentic/defense/whirlpool/helpers/whirlpool_engine_spec.rb +227 -0
  217. data/spec/legion/extensions/agentic/defense/whirlpool/runners/cognitive_whirlpool_spec.rb +226 -0
  218. data/spec/spec_helper.rb +46 -0
  219. metadata +303 -0
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Defense
7
+ module Bias
8
+ module Helpers
9
+ class BiasDetector
10
+ include Constants
11
+
12
+ def initialize
13
+ @susceptibility = Constants::BIAS_TYPES.to_h { |b| [b, Constants::DEFAULT_SUSCEPTIBILITY] }
14
+ end
15
+
16
+ def detect_anchoring(current_value:, anchor_value:, domain: nil) # rubocop:disable Lint/UnusedMethodArgument
17
+ return 0.0 if anchor_value.nil? || anchor_value.zero?
18
+
19
+ distance = (current_value - anchor_value).abs.to_f / anchor_value.abs
20
+ pull = 1.0 - distance.clamp(0.0, 1.0)
21
+ magnitude = pull * susceptibility_for(:anchoring)
22
+ update_susceptibility(:anchoring, detected: magnitude >= Constants::DETECTION_THRESHOLD)
23
+ magnitude.clamp(0.0, 1.0)
24
+ end
25
+
26
+ def detect_confirmation(evidence_direction:, hypothesis_direction:, domain: nil) # rubocop:disable Lint/UnusedMethodArgument
27
+ magnitude = if evidence_direction == hypothesis_direction
28
+ Constants::CONFIRMATION_WEIGHT * susceptibility_for(:confirmation)
29
+ else
30
+ (1.0 - Constants::CONFIRMATION_WEIGHT) * susceptibility_for(:confirmation)
31
+ end
32
+ update_susceptibility(:confirmation, detected: magnitude >= Constants::DETECTION_THRESHOLD)
33
+ magnitude.clamp(0.0, 1.0)
34
+ end
35
+
36
+ def detect_availability(recent_events:, domain: nil) # rubocop:disable Lint/UnusedMethodArgument
37
+ window = Constants::AVAILABILITY_RECENCY_WINDOW
38
+ density = [recent_events.size, window].min.to_f / window
39
+ magnitude = density * susceptibility_for(:availability)
40
+ update_susceptibility(:availability, detected: magnitude >= Constants::DETECTION_THRESHOLD)
41
+ magnitude.clamp(0.0, 1.0)
42
+ end
43
+
44
+ def detect_recency(data_points:, domain: nil) # rubocop:disable Lint/UnusedMethodArgument
45
+ return 0.0 if data_points.size < 2
46
+
47
+ total = data_points.size
48
+ half = total / 2
49
+ recent_half = data_points.last(half)
50
+ earlier_half = data_points.first(half)
51
+
52
+ recent_mean = mean(recent_half)
53
+ earlier_mean = mean(earlier_half)
54
+
55
+ range = (data_points.max - data_points.min).to_f
56
+ return 0.0 if range.zero?
57
+
58
+ skew = (recent_mean - earlier_mean).abs / range
59
+ magnitude = skew * susceptibility_for(:recency)
60
+ update_susceptibility(:recency, detected: magnitude >= Constants::DETECTION_THRESHOLD)
61
+ magnitude.clamp(0.0, 1.0)
62
+ end
63
+
64
+ def detect_sunk_cost(invested:, expected_return:, domain: nil) # rubocop:disable Lint/UnusedMethodArgument
65
+ return 0.0 if invested <= 0
66
+
67
+ ratio = invested.to_f / (invested + expected_return.abs + 1.0)
68
+ magnitude = ratio * susceptibility_for(:sunk_cost)
69
+ update_susceptibility(:sunk_cost, detected: magnitude >= Constants::DETECTION_THRESHOLD)
70
+ magnitude.clamp(0.0, 1.0)
71
+ end
72
+
73
+ def susceptibility_for(bias_type)
74
+ @susceptibility.fetch(bias_type, Constants::DEFAULT_SUSCEPTIBILITY)
75
+ end
76
+
77
+ def update_susceptibility(bias_type, detected:)
78
+ return unless @susceptibility.key?(bias_type)
79
+
80
+ alpha = Constants::SUSCEPTIBILITY_ALPHA
81
+ signal = detected ? 1.0 : 0.0
82
+ current = @susceptibility[bias_type]
83
+ @susceptibility[bias_type] = ((alpha * signal) + ((1.0 - alpha) * current)).clamp(0.0, 1.0)
84
+ end
85
+
86
+ def correction_for(magnitude)
87
+ (magnitude * Constants::CORRECTION_FACTOR).clamp(0.0, 1.0)
88
+ end
89
+
90
+ def to_h
91
+ { susceptibility: @susceptibility.dup }
92
+ end
93
+
94
+ private
95
+
96
+ def mean(values)
97
+ return 0.0 if values.empty?
98
+
99
+ values.sum.to_f / values.size
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Agentic
8
+ module Defense
9
+ module Bias
10
+ module Helpers
11
+ class BiasEvent
12
+ attr_reader :id, :bias_type, :domain, :magnitude, :corrected,
13
+ :correction_applied, :context, :timestamp
14
+
15
+ def initialize(bias_type:, domain:, magnitude:, **opts)
16
+ @id = SecureRandom.uuid
17
+ @bias_type = bias_type
18
+ @domain = domain
19
+ @magnitude = magnitude
20
+ @corrected = opts.fetch(:corrected, false)
21
+ @correction_applied = opts.fetch(:correction_applied, 0.0)
22
+ @context = opts.fetch(:context, {})
23
+ @timestamp = Time.now.utc
24
+ end
25
+
26
+ def to_h
27
+ {
28
+ id: @id,
29
+ bias_type: @bias_type,
30
+ domain: @domain,
31
+ magnitude: @magnitude,
32
+ corrected: @corrected,
33
+ correction_applied: @correction_applied,
34
+ context: @context,
35
+ timestamp: @timestamp
36
+ }
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Defense
7
+ module Bias
8
+ module Helpers
9
+ class BiasStore
10
+ def initialize
11
+ @events = []
12
+ @anchors = {} # domain -> array of { value:, influence:, recorded_at: }
13
+ end
14
+
15
+ def record(event)
16
+ @events << event
17
+ @events.shift while @events.size > Constants::MAX_BIAS_EVENTS
18
+ event
19
+ end
20
+
21
+ def recent(count = 10)
22
+ @events.last(count).map(&:to_h)
23
+ end
24
+
25
+ def by_type(bias_type)
26
+ @events.select { |e| e.bias_type == bias_type }.map(&:to_h)
27
+ end
28
+
29
+ def by_domain(domain)
30
+ @events.select { |e| e.domain == domain }.map(&:to_h)
31
+ end
32
+
33
+ def register_anchor(domain, value:, influence: 1.0)
34
+ @anchors[domain] ||= []
35
+ @anchors[domain] << { value: value, influence: influence, recorded_at: Time.now.utc }
36
+ @anchors[domain].shift while @anchors[domain].size > Constants::MAX_ANCHORS
37
+ end
38
+
39
+ def anchors_for(domain)
40
+ @anchors.fetch(domain, [])
41
+ end
42
+
43
+ def decay_anchors
44
+ @anchors.each_value do |anchor_list|
45
+ anchor_list.each { |a| a[:influence] = (a[:influence] - Constants::ANCHOR_DECAY).clamp(0.0, 1.0) }
46
+ anchor_list.reject! { |a| a[:influence] <= 0.0 }
47
+ end
48
+ end
49
+
50
+ def stats
51
+ return { total: 0, by_type: {}, by_domain: {} } if @events.empty?
52
+
53
+ by_type = Constants::BIAS_TYPES.to_h do |bt|
54
+ events = @events.select { |e| e.bias_type == bt }
55
+ avg_mag = events.empty? ? 0.0 : events.sum(&:magnitude) / events.size
56
+ [bt, { count: events.size, avg_magnitude: avg_mag.round(4) }]
57
+ end
58
+
59
+ domains = @events.map(&:domain).uniq
60
+ by_domain = domains.to_h do |d|
61
+ events = @events.select { |e| e.domain == d }
62
+ [d, { count: events.size }]
63
+ end
64
+
65
+ {
66
+ total: @events.size,
67
+ by_type: by_type,
68
+ by_domain: by_domain
69
+ }
70
+ end
71
+
72
+ def to_h
73
+ {
74
+ total_events: @events.size,
75
+ anchor_domains: @anchors.keys
76
+ }
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Defense
7
+ module Bias
8
+ module Helpers
9
+ module Constants
10
+ BIAS_TYPES = %i[anchoring confirmation availability recency sunk_cost].freeze
11
+
12
+ DETECTION_THRESHOLD = 0.3 # above this = bias likely influencing decision
13
+ CORRECTION_FACTOR = 0.5 # how much to correct when bias detected
14
+ DEFAULT_SUSCEPTIBILITY = 0.5 # starting susceptibility per bias
15
+ SUSCEPTIBILITY_ALPHA = 0.1 # EMA alpha for updating susceptibility
16
+ DECAY_RATE = 0.02 # how fast bias activation decays per tick
17
+ MAX_BIAS_EVENTS = 200 # max tracked bias events
18
+ MAX_ANCHORS = 50 # max tracked anchor values
19
+ ANCHOR_DECAY = 0.05 # how fast anchor influence decays
20
+ CONFIRMATION_WEIGHT = 0.7 # weight of confirming vs disconfirming evidence
21
+ AVAILABILITY_RECENCY_WINDOW = 10 # recent events window for availability heuristic
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Defense
7
+ module Bias
8
+ module Runners
9
+ module Bias
10
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
11
+ Legion::Extensions::Helpers.const_defined?(:Lex)
12
+
13
+ def check_for_bias(domain:, decision_context: {}, **)
14
+ Legion::Logging.debug "[bias] check_for_bias domain=#{domain}"
15
+ detected = collect_bias_detections(domain, decision_context)
16
+ active = detected.select { |b| b[:magnitude] >= Helpers::Constants::DETECTION_THRESHOLD }
17
+ Legion::Logging.debug "[bias] check_for_bias domain=#{domain} detected=#{active.size}"
18
+ { success: true, domain: domain, detected: active, all: detected }
19
+ end
20
+
21
+ def record_anchor(domain:, value:, **)
22
+ bias_store.register_anchor(domain, value: value)
23
+ Legion::Logging.debug "[bias] anchor recorded domain=#{domain} value=#{value}"
24
+ { success: true, domain: domain, value: value }
25
+ end
26
+
27
+ def update_bias(**)
28
+ bias_store.decay_anchors
29
+ Legion::Logging.debug '[bias] update_bias: anchors decayed'
30
+ { success: true }
31
+ end
32
+
33
+ def bias_report(domain: nil, **)
34
+ events = domain ? bias_store.by_domain(domain) : bias_store.recent(50)
35
+ Legion::Logging.debug "[bias] bias_report domain=#{domain.inspect} events=#{events.size}"
36
+ { success: true, domain: domain, events: events, count: events.size }
37
+ end
38
+
39
+ def susceptibility_profile(**)
40
+ profile = bias_detector.to_h
41
+ Legion::Logging.debug '[bias] susceptibility_profile'
42
+ { success: true, **profile }
43
+ end
44
+
45
+ def bias_stats(**)
46
+ stats = bias_store.stats
47
+ Legion::Logging.debug "[bias] bias_stats total=#{stats[:total]}"
48
+ { success: true, **stats }
49
+ end
50
+
51
+ private
52
+
53
+ def bias_detector
54
+ @bias_detector ||= Helpers::BiasDetector.new
55
+ end
56
+
57
+ def bias_store
58
+ @bias_store ||= Helpers::BiasStore.new
59
+ end
60
+
61
+ def collect_bias_detections(domain, ctx)
62
+ results = []
63
+ results.concat(detect_anchoring_bias(domain, ctx))
64
+ results.concat(detect_confirmation_bias(domain, ctx))
65
+ results.concat(detect_availability_bias(domain, ctx))
66
+ results.concat(detect_recency_bias(domain, ctx))
67
+ results.concat(detect_sunk_cost_bias(domain, ctx))
68
+ results
69
+ end
70
+
71
+ def detect_anchoring_bias(domain, ctx)
72
+ anchors = bias_store.anchors_for(domain)
73
+ return [] unless anchors.any? && ctx[:current_value]
74
+
75
+ anchor_value = anchors.max_by { |a| a[:influence] }&.dig(:value)
76
+ return [] unless anchor_value
77
+
78
+ mag = bias_detector.detect_anchoring(
79
+ current_value: ctx[:current_value],
80
+ anchor_value: anchor_value,
81
+ domain: domain
82
+ )
83
+ [build_bias_result(:anchoring, domain, mag, ctx)]
84
+ end
85
+
86
+ def detect_confirmation_bias(domain, ctx)
87
+ return [] unless ctx[:evidence_direction] && ctx[:hypothesis_direction]
88
+
89
+ mag = bias_detector.detect_confirmation(
90
+ evidence_direction: ctx[:evidence_direction],
91
+ hypothesis_direction: ctx[:hypothesis_direction],
92
+ domain: domain
93
+ )
94
+ [build_bias_result(:confirmation, domain, mag, ctx)]
95
+ end
96
+
97
+ def detect_availability_bias(domain, ctx)
98
+ return [] unless ctx[:recent_events]
99
+
100
+ mag = bias_detector.detect_availability(recent_events: ctx[:recent_events], domain: domain)
101
+ [build_bias_result(:availability, domain, mag, ctx)]
102
+ end
103
+
104
+ def detect_recency_bias(domain, ctx)
105
+ return [] unless ctx[:data_points]
106
+
107
+ mag = bias_detector.detect_recency(data_points: ctx[:data_points], domain: domain)
108
+ [build_bias_result(:recency, domain, mag, ctx)]
109
+ end
110
+
111
+ def detect_sunk_cost_bias(domain, ctx)
112
+ return [] unless ctx[:invested] && !ctx[:expected_return].nil?
113
+
114
+ mag = bias_detector.detect_sunk_cost(
115
+ invested: ctx[:invested],
116
+ expected_return: ctx[:expected_return],
117
+ domain: domain
118
+ )
119
+ [build_bias_result(:sunk_cost, domain, mag, ctx)]
120
+ end
121
+
122
+ def build_bias_result(bias_type, domain, magnitude, context)
123
+ correction = bias_detector.correction_for(magnitude)
124
+ corrected = magnitude >= Helpers::Constants::DETECTION_THRESHOLD
125
+
126
+ if corrected
127
+ event = Helpers::BiasEvent.new(
128
+ bias_type: bias_type,
129
+ domain: domain,
130
+ magnitude: magnitude,
131
+ corrected: corrected,
132
+ correction_applied: correction,
133
+ context: context
134
+ )
135
+ bias_store.record(event)
136
+ end
137
+
138
+ {
139
+ bias_type: bias_type,
140
+ magnitude: magnitude,
141
+ corrected: corrected,
142
+ correction_applied: correction
143
+ }
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Defense
7
+ module Bias
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/defense/bias/version'
4
+ require 'legion/extensions/agentic/defense/bias/helpers/constants'
5
+ require 'legion/extensions/agentic/defense/bias/helpers/bias_event'
6
+ require 'legion/extensions/agentic/defense/bias/helpers/bias_detector'
7
+ require 'legion/extensions/agentic/defense/bias/helpers/bias_store'
8
+ require 'legion/extensions/agentic/defense/bias/runners/bias'
9
+ require 'legion/extensions/agentic/defense/bias/client'
10
+
11
+ module Legion
12
+ module Extensions
13
+ module Agentic
14
+ module Defense
15
+ module Bias
16
+ end
17
+ end
18
+ end
19
+ end
20
+ 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 Defense
9
+ module Confabulation
10
+ module Actor
11
+ class Decay < Legion::Extensions::Actors::Every
12
+ def runner_class
13
+ Legion::Extensions::Agentic::Defense::Confabulation::Runners::Confabulation
14
+ end
15
+
16
+ def runner_function
17
+ 'confabulation_report'
18
+ end
19
+
20
+ def time
21
+ 300
22
+ end
23
+
24
+ def run_now?
25
+ false
26
+ end
27
+
28
+ def use_runner?
29
+ false
30
+ end
31
+
32
+ def check_subtask?
33
+ false
34
+ end
35
+
36
+ def generate_task?
37
+ false
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/defense/confabulation/helpers/constants'
4
+ require 'legion/extensions/agentic/defense/confabulation/helpers/claim'
5
+ require 'legion/extensions/agentic/defense/confabulation/helpers/confabulation_engine'
6
+ require 'legion/extensions/agentic/defense/confabulation/runners/confabulation'
7
+
8
+ module Legion
9
+ module Extensions
10
+ module Agentic
11
+ module Defense
12
+ module Confabulation
13
+ class Client
14
+ include Runners::Confabulation
15
+
16
+ def initialize(**)
17
+ @confabulation_engine = Helpers::ConfabulationEngine.new
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :confabulation_engine
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Agentic
8
+ module Defense
9
+ module Confabulation
10
+ module Helpers
11
+ class Claim
12
+ attr_reader :id, :content, :claim_type, :confidence, :evidence_strength,
13
+ :verified, :confabulated, :created_at
14
+
15
+ def initialize(content:, claim_type:, confidence:, evidence_strength:)
16
+ @id = SecureRandom.uuid
17
+ @content = content
18
+ @claim_type = claim_type
19
+ @confidence = confidence.clamp(0.0, 1.0)
20
+ @evidence_strength = evidence_strength.clamp(0.0, 1.0)
21
+ @verified = false
22
+ @confabulated = false
23
+ @created_at = Time.now.utc
24
+ end
25
+
26
+ def confabulation_risk
27
+ (confidence - evidence_strength).clamp(0.0, 1.0)
28
+ end
29
+
30
+ def verify!
31
+ @verified = true
32
+ self
33
+ end
34
+
35
+ def mark_confabulated!
36
+ @confabulated = true
37
+ self
38
+ end
39
+
40
+ def risk_label
41
+ Constants::RISK_LABELS.each do |range, label|
42
+ return label if range.cover?(confabulation_risk)
43
+ end
44
+ :extreme
45
+ end
46
+
47
+ def to_h
48
+ {
49
+ id: id,
50
+ content: content,
51
+ claim_type: claim_type,
52
+ confidence: confidence.round(10),
53
+ evidence_strength: evidence_strength.round(10),
54
+ confabulation_risk: confabulation_risk.round(10),
55
+ risk_label: risk_label,
56
+ verified: verified,
57
+ confabulated: confabulated,
58
+ created_at: created_at.iso8601
59
+ }
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end