lex-cognitive-immunology 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.
- checksums.yaml +7 -0
- data/Gemfile +11 -0
- data/lex-cognitive-immunology.gemspec +29 -0
- data/lib/legion/extensions/cognitive_immunology/client.rb +25 -0
- data/lib/legion/extensions/cognitive_immunology/helpers/antibody.rb +51 -0
- data/lib/legion/extensions/cognitive_immunology/helpers/constants.rb +39 -0
- data/lib/legion/extensions/cognitive_immunology/helpers/immune_engine.rb +183 -0
- data/lib/legion/extensions/cognitive_immunology/helpers/threat.rb +63 -0
- data/lib/legion/extensions/cognitive_immunology/runners/cognitive_immunology.rb +88 -0
- data/lib/legion/extensions/cognitive_immunology/version.rb +9 -0
- data/lib/legion/extensions/cognitive_immunology.rb +16 -0
- data/spec/legion/extensions/cognitive_immunology/client_spec.rb +61 -0
- data/spec/legion/extensions/cognitive_immunology/helpers/antibody_spec.rb +98 -0
- data/spec/legion/extensions/cognitive_immunology/helpers/constants_spec.rb +86 -0
- data/spec/legion/extensions/cognitive_immunology/helpers/immune_engine_spec.rb +275 -0
- data/spec/legion/extensions/cognitive_immunology/helpers/threat_spec.rb +133 -0
- data/spec/legion/extensions/cognitive_immunology/runners/cognitive_immunology_spec.rb +177 -0
- data/spec/spec_helper.rb +20 -0
- metadata +78 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 5ef857f308ed2f088519c8105a9875d622ea045760b68e44c789bd2903288af0
|
|
4
|
+
data.tar.gz: 71b3c2d6846f87bdce12a781b83f74276550bc796fc72337a3ce9726c5adc952
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 0ebd1e41f872b4d8bdffd40f424bb1850bf25f3577810289c3c6c181bc30cebf34a0b1bf81ae07cad37767ed4a41e7d5c8020c6c6fcb04c078b9c24d140a49ae
|
|
7
|
+
data.tar.gz: 7397a05f8c5ba4b258cfda02e66c49a83e8711b596426cbae5d6a048a70a905baa59666e870f2619c38277a1d85ecc8f22cced31b42e9a474e2dcfbf20d694da
|
data/Gemfile
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/cognitive_immunology/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lex-cognitive-immunology'
|
|
7
|
+
spec.version = Legion::Extensions::CognitiveImmunology::VERSION
|
|
8
|
+
spec.authors = ['Esity']
|
|
9
|
+
spec.email = ['matthewdiverson@gmail.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'LEX Cognitive Immunology'
|
|
12
|
+
spec.description = 'Cognitive immune system for brain-modeled agentic AI — defense against memetic threats, manipulation, and cognitive viruses'
|
|
13
|
+
spec.homepage = 'https://github.com/LegionIO/lex-cognitive-immunology'
|
|
14
|
+
spec.license = 'MIT'
|
|
15
|
+
spec.required_ruby_version = '>= 3.4'
|
|
16
|
+
|
|
17
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
18
|
+
spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-cognitive-immunology'
|
|
19
|
+
spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-cognitive-immunology'
|
|
20
|
+
spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-cognitive-immunology'
|
|
21
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-cognitive-immunology/issues'
|
|
22
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
23
|
+
|
|
24
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
25
|
+
Dir.glob('{lib,spec}/**/*') + %w[lex-cognitive-immunology.gemspec Gemfile]
|
|
26
|
+
end
|
|
27
|
+
spec.require_paths = ['lib']
|
|
28
|
+
spec.add_development_dependency 'legion-gaia'
|
|
29
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/cognitive_immunology/helpers/constants'
|
|
4
|
+
require 'legion/extensions/cognitive_immunology/helpers/threat'
|
|
5
|
+
require 'legion/extensions/cognitive_immunology/helpers/antibody'
|
|
6
|
+
require 'legion/extensions/cognitive_immunology/helpers/immune_engine'
|
|
7
|
+
require 'legion/extensions/cognitive_immunology/runners/cognitive_immunology'
|
|
8
|
+
|
|
9
|
+
module Legion
|
|
10
|
+
module Extensions
|
|
11
|
+
module CognitiveImmunology
|
|
12
|
+
class Client
|
|
13
|
+
include Runners::CognitiveImmunology
|
|
14
|
+
|
|
15
|
+
def initialize(**)
|
|
16
|
+
@engine = Helpers::ImmuneEngine.new
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
attr_reader :engine
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module CognitiveImmunology
|
|
8
|
+
module Helpers
|
|
9
|
+
class Antibody
|
|
10
|
+
attr_reader :id, :tactic, :pattern, :created_at
|
|
11
|
+
attr_accessor :strength, :matches
|
|
12
|
+
|
|
13
|
+
def initialize(tactic:, pattern:, strength: 0.5)
|
|
14
|
+
@id = SecureRandom.uuid
|
|
15
|
+
@tactic = tactic
|
|
16
|
+
@pattern = pattern
|
|
17
|
+
@strength = strength.clamp(0.0, 1.0)
|
|
18
|
+
@matches = 0
|
|
19
|
+
@created_at = Time.now.utc
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def match!
|
|
23
|
+
@matches += 1
|
|
24
|
+
boost = Constants::RESISTANCE_BOOST / (@matches + 1)
|
|
25
|
+
@strength = (@strength + boost.round(10)).clamp(0.0, 1.0).round(10)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def decay!
|
|
29
|
+
@strength = (@strength - Constants::RESISTANCE_DECAY).clamp(0.0, 1.0).round(10)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def effective?
|
|
33
|
+
@strength >= 0.3
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def to_h
|
|
37
|
+
{
|
|
38
|
+
id: @id,
|
|
39
|
+
tactic: @tactic,
|
|
40
|
+
pattern: @pattern,
|
|
41
|
+
strength: @strength,
|
|
42
|
+
matches: @matches,
|
|
43
|
+
effective: effective?,
|
|
44
|
+
created_at: @created_at
|
|
45
|
+
}
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveImmunology
|
|
6
|
+
module Helpers
|
|
7
|
+
module Constants
|
|
8
|
+
DEFAULT_RESISTANCE = 0.5
|
|
9
|
+
RESISTANCE_BOOST = 0.1
|
|
10
|
+
RESISTANCE_DECAY = 0.02
|
|
11
|
+
MAX_THREATS = 500
|
|
12
|
+
MAX_ANTIBODIES = 200
|
|
13
|
+
|
|
14
|
+
THREAT_LABELS = {
|
|
15
|
+
(0.8..) => :critical,
|
|
16
|
+
(0.6...0.8) => :severe,
|
|
17
|
+
(0.4...0.6) => :moderate,
|
|
18
|
+
(0.2...0.4) => :low,
|
|
19
|
+
(..0.2) => :negligible
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
IMMUNITY_LABELS = {
|
|
23
|
+
(0.8..) => :immune,
|
|
24
|
+
(0.6...0.8) => :resistant,
|
|
25
|
+
(0.4...0.6) => :normal,
|
|
26
|
+
(0.2...0.4) => :vulnerable,
|
|
27
|
+
(..0.2) => :compromised
|
|
28
|
+
}.freeze
|
|
29
|
+
|
|
30
|
+
MANIPULATION_TACTICS = %i[
|
|
31
|
+
authority_appeal emotional_blackmail false_urgency
|
|
32
|
+
social_proof_abuse gaslighting strawman ad_hominem
|
|
33
|
+
sunk_cost_exploit bandwagon fear_mongering
|
|
34
|
+
].freeze
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveImmunology
|
|
6
|
+
module Helpers
|
|
7
|
+
class ImmuneEngine
|
|
8
|
+
attr_reader :resistance, :inflammatory
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@threats = {}
|
|
12
|
+
@antibodies = {}
|
|
13
|
+
@resistance = Constants::DEFAULT_RESISTANCE
|
|
14
|
+
@inflammatory = false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def detect_threat(source:, tactic:, content_hash:, threat_level: 0.5)
|
|
18
|
+
prune_threats_if_full
|
|
19
|
+
|
|
20
|
+
threat = Threat.new(
|
|
21
|
+
source: source,
|
|
22
|
+
tactic: tactic,
|
|
23
|
+
content_hash: content_hash,
|
|
24
|
+
threat_level: threat_level
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
matched = match_antibodies_for_tactic(tactic)
|
|
28
|
+
matched.each do |ab|
|
|
29
|
+
ab.match!
|
|
30
|
+
reduction = (ab.strength * 0.2).round(10)
|
|
31
|
+
threat.threat_level = (threat.threat_level - reduction).clamp(0.0, 1.0).round(10)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
@threats[threat.id] = threat
|
|
35
|
+
Legion::Logging.debug "[cognitive_immunology] threat detected: id=#{threat.id} tactic=#{tactic} level=#{threat.threat_level.round(2)}"
|
|
36
|
+
threat
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def quarantine_threat(threat_id:)
|
|
40
|
+
threat = @threats.fetch(threat_id, nil)
|
|
41
|
+
return { success: false, reason: 'not found' } unless threat
|
|
42
|
+
|
|
43
|
+
threat.quarantine!
|
|
44
|
+
Legion::Logging.info "[cognitive_immunology] quarantined: id=#{threat_id} tactic=#{threat.tactic}"
|
|
45
|
+
{ success: true, threat_id: threat_id }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def release_threat(threat_id:)
|
|
49
|
+
threat = @threats.fetch(threat_id, nil)
|
|
50
|
+
return { success: false, reason: 'not found' } unless threat
|
|
51
|
+
|
|
52
|
+
threat.release!
|
|
53
|
+
Legion::Logging.debug "[cognitive_immunology] released: id=#{threat_id}"
|
|
54
|
+
{ success: true, threat_id: threat_id }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def inoculate(threat_id:)
|
|
58
|
+
threat = @threats.fetch(threat_id, nil)
|
|
59
|
+
return { success: false, reason: 'not found' } unless threat
|
|
60
|
+
|
|
61
|
+
threat.expose!
|
|
62
|
+
boost = (Constants::RESISTANCE_BOOST / (threat.exposure_count + 1)).round(10)
|
|
63
|
+
@resistance = (@resistance + boost).clamp(0.0, 1.0).round(10)
|
|
64
|
+
Legion::Logging.debug "[cognitive_immunology] inoculate: id=#{threat_id} exposure=#{threat.exposure_count} resistance=#{@resistance.round(2)}"
|
|
65
|
+
{ success: true, threat_id: threat_id, exposure_count: threat.exposure_count, resistance: @resistance }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def create_antibody(tactic:, pattern:, strength: 0.5)
|
|
69
|
+
prune_antibodies_if_full
|
|
70
|
+
|
|
71
|
+
ab = Antibody.new(tactic: tactic, pattern: pattern, strength: strength)
|
|
72
|
+
@antibodies[ab.id] = ab
|
|
73
|
+
Legion::Logging.info "[cognitive_immunology] antibody created: id=#{ab.id} tactic=#{tactic} strength=#{strength}"
|
|
74
|
+
ab
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def scan_for_tactic(tactic:)
|
|
78
|
+
@threats.values.select { |t| t.tactic == tactic }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def trigger_inflammatory_response
|
|
82
|
+
@inflammatory = true
|
|
83
|
+
Legion::Logging.warn '[cognitive_immunology] inflammatory response triggered — heightened scrutiny mode'
|
|
84
|
+
{ inflammatory: true }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def resolve_inflammation
|
|
88
|
+
@inflammatory = false
|
|
89
|
+
Legion::Logging.info '[cognitive_immunology] inflammation resolved — returning to normal scrutiny'
|
|
90
|
+
{ inflammatory: false }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def overall_immunity
|
|
94
|
+
ab_coverage = antibody_coverage_score
|
|
95
|
+
score = ((@resistance * 0.6) + (ab_coverage * 0.4)).round(10)
|
|
96
|
+
score.clamp(0.0, 1.0)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def immunity_label
|
|
100
|
+
score = overall_immunity
|
|
101
|
+
Constants::IMMUNITY_LABELS.find { |range, _| range.cover?(score) }&.last || :compromised
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def vulnerability_report
|
|
105
|
+
covered_tactics = @antibodies.values.map(&:tactic).uniq
|
|
106
|
+
uncovered = Constants::MANIPULATION_TACTICS.reject { |t| covered_tactics.include?(t) }
|
|
107
|
+
{
|
|
108
|
+
covered: covered_tactics,
|
|
109
|
+
uncovered: uncovered,
|
|
110
|
+
coverage: (covered_tactics.size.to_f / Constants::MANIPULATION_TACTICS.size).round(10)
|
|
111
|
+
}
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def threat_history(limit: 10)
|
|
115
|
+
@threats.values
|
|
116
|
+
.sort_by(&:created_at)
|
|
117
|
+
.last(limit)
|
|
118
|
+
.map(&:to_h)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def decay_all
|
|
122
|
+
@antibodies.each_value(&:decay!)
|
|
123
|
+
@resistance = (@resistance - Constants::RESISTANCE_DECAY).clamp(0.0, 1.0).round(10)
|
|
124
|
+
Legion::Logging.debug "[cognitive_immunology] decay cycle: resistance=#{@resistance.round(2)} antibodies=#{@antibodies.size}"
|
|
125
|
+
{ resistance: @resistance, antibodies_decayed: @antibodies.size }
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def prune_ineffective
|
|
129
|
+
before = @antibodies.size
|
|
130
|
+
@antibodies.select! { |_, ab| ab.effective? }
|
|
131
|
+
pruned = before - @antibodies.size
|
|
132
|
+
Legion::Logging.debug "[cognitive_immunology] pruned #{pruned} ineffective antibodies"
|
|
133
|
+
{ pruned: pruned, remaining: @antibodies.size }
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def to_h
|
|
137
|
+
{
|
|
138
|
+
threat_count: @threats.size,
|
|
139
|
+
quarantined_count: @threats.values.count(&:quarantined),
|
|
140
|
+
antibody_count: @antibodies.size,
|
|
141
|
+
effective_antibody_count: @antibodies.values.count(&:effective?),
|
|
142
|
+
resistance: @resistance,
|
|
143
|
+
inflammatory: @inflammatory,
|
|
144
|
+
overall_immunity: overall_immunity,
|
|
145
|
+
immunity_label: immunity_label
|
|
146
|
+
}
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
private
|
|
150
|
+
|
|
151
|
+
def match_antibodies_for_tactic(tactic)
|
|
152
|
+
@antibodies.values.select { |ab| ab.tactic == tactic && ab.effective? }
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def antibody_coverage_score
|
|
156
|
+
return 0.0 if @antibodies.empty?
|
|
157
|
+
|
|
158
|
+
effective = @antibodies.values.select(&:effective?)
|
|
159
|
+
return 0.0 if effective.empty?
|
|
160
|
+
|
|
161
|
+
avg_strength = effective.sum(&:strength) / effective.size.to_f
|
|
162
|
+
tactic_coverage = vulnerability_report[:coverage]
|
|
163
|
+
((avg_strength * 0.5) + (tactic_coverage * 0.5)).round(10)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def prune_threats_if_full
|
|
167
|
+
return unless @threats.size >= Constants::MAX_THREATS
|
|
168
|
+
|
|
169
|
+
oldest = @threats.values.min_by(&:created_at)
|
|
170
|
+
@threats.delete(oldest.id) if oldest
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def prune_antibodies_if_full
|
|
174
|
+
return unless @antibodies.size >= Constants::MAX_ANTIBODIES
|
|
175
|
+
|
|
176
|
+
weakest = @antibodies.values.min_by(&:strength)
|
|
177
|
+
@antibodies.delete(weakest.id) if weakest
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module CognitiveImmunology
|
|
8
|
+
module Helpers
|
|
9
|
+
class Threat
|
|
10
|
+
attr_reader :id, :source, :tactic, :content_hash, :created_at
|
|
11
|
+
attr_accessor :threat_level, :quarantined, :exposure_count
|
|
12
|
+
|
|
13
|
+
def initialize(source:, tactic:, content_hash:, threat_level: 0.5)
|
|
14
|
+
@id = SecureRandom.uuid
|
|
15
|
+
@source = source
|
|
16
|
+
@tactic = tactic
|
|
17
|
+
@content_hash = content_hash
|
|
18
|
+
@threat_level = threat_level.clamp(0.0, 1.0)
|
|
19
|
+
@quarantined = false
|
|
20
|
+
@exposure_count = 0
|
|
21
|
+
@created_at = Time.now.utc
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def threat_label
|
|
25
|
+
Constants::THREAT_LABELS.find { |range, _| range.cover?(@threat_level) }&.last || :negligible
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def quarantine!
|
|
29
|
+
@quarantined = true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def release!
|
|
33
|
+
@quarantined = false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def expose!
|
|
37
|
+
@exposure_count += 1
|
|
38
|
+
reduction = (0.05 / (@exposure_count + 1)).round(10)
|
|
39
|
+
@threat_level = (@threat_level - reduction).clamp(0.0, 1.0).round(10)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def escalate!(amount: 0.1)
|
|
43
|
+
@threat_level = (@threat_level + amount).clamp(0.0, 1.0).round(10)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def to_h
|
|
47
|
+
{
|
|
48
|
+
id: @id,
|
|
49
|
+
source: @source,
|
|
50
|
+
tactic: @tactic,
|
|
51
|
+
content_hash: @content_hash,
|
|
52
|
+
threat_level: @threat_level,
|
|
53
|
+
threat_label: threat_label,
|
|
54
|
+
quarantined: @quarantined,
|
|
55
|
+
exposure_count: @exposure_count,
|
|
56
|
+
created_at: @created_at
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveImmunology
|
|
6
|
+
module Runners
|
|
7
|
+
module CognitiveImmunology
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
9
|
+
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
|
|
11
|
+
def detect_threat(source:, tactic:, content_hash:, threat_level: 0.5, **)
|
|
12
|
+
threat = engine.detect_threat(source: source, tactic: tactic, content_hash: content_hash, threat_level: threat_level)
|
|
13
|
+
{ success: true, threat: threat.to_h }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def quarantine_threat(threat_id:, **)
|
|
17
|
+
result = engine.quarantine_threat(threat_id: threat_id)
|
|
18
|
+
result.merge(success: result.fetch(:success, false))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def release_threat(threat_id:, **)
|
|
22
|
+
result = engine.release_threat(threat_id: threat_id)
|
|
23
|
+
result.merge(success: result.fetch(:success, false))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def inoculate(threat_id:, **)
|
|
27
|
+
engine.inoculate(threat_id: threat_id)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def create_antibody(tactic:, pattern:, strength: 0.5, **)
|
|
31
|
+
ab = engine.create_antibody(tactic: tactic, pattern: pattern, strength: strength)
|
|
32
|
+
{ success: true, antibody: ab.to_h }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def scan_for_tactic(tactic:, **)
|
|
36
|
+
threats = engine.scan_for_tactic(tactic: tactic)
|
|
37
|
+
{ success: true, tactic: tactic, threats: threats.map(&:to_h), count: threats.size }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def trigger_inflammatory_response(**)
|
|
41
|
+
result = engine.trigger_inflammatory_response
|
|
42
|
+
{ success: true }.merge(result)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def resolve_inflammation(**)
|
|
46
|
+
result = engine.resolve_inflammation
|
|
47
|
+
{ success: true }.merge(result)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def overall_immunity(**)
|
|
51
|
+
score = engine.overall_immunity
|
|
52
|
+
{ success: true, score: score, label: engine.immunity_label }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def vulnerability_report(**)
|
|
56
|
+
report = engine.vulnerability_report
|
|
57
|
+
{ success: true }.merge(report)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def threat_history(limit: 10, **)
|
|
61
|
+
threats = engine.threat_history(limit: limit)
|
|
62
|
+
{ success: true, threats: threats, count: threats.size }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def decay_all(**)
|
|
66
|
+
result = engine.decay_all
|
|
67
|
+
{ success: true }.merge(result)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def prune_ineffective(**)
|
|
71
|
+
result = engine.prune_ineffective
|
|
72
|
+
{ success: true }.merge(result)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def immune_status(**)
|
|
76
|
+
{ success: true }.merge(engine.to_h)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def engine
|
|
82
|
+
@engine ||= Helpers::ImmuneEngine.new
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/cognitive_immunology/version'
|
|
4
|
+
require 'legion/extensions/cognitive_immunology/helpers/constants'
|
|
5
|
+
require 'legion/extensions/cognitive_immunology/helpers/threat'
|
|
6
|
+
require 'legion/extensions/cognitive_immunology/helpers/antibody'
|
|
7
|
+
require 'legion/extensions/cognitive_immunology/helpers/immune_engine'
|
|
8
|
+
require 'legion/extensions/cognitive_immunology/runners/cognitive_immunology'
|
|
9
|
+
|
|
10
|
+
module Legion
|
|
11
|
+
module Extensions
|
|
12
|
+
module CognitiveImmunology
|
|
13
|
+
extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::CognitiveImmunology::Client do
|
|
4
|
+
let(:client) { described_class.new }
|
|
5
|
+
|
|
6
|
+
it 'responds to detect_threat' do
|
|
7
|
+
expect(client).to respond_to(:detect_threat)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'responds to quarantine_threat' do
|
|
11
|
+
expect(client).to respond_to(:quarantine_threat)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'responds to release_threat' do
|
|
15
|
+
expect(client).to respond_to(:release_threat)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'responds to inoculate' do
|
|
19
|
+
expect(client).to respond_to(:inoculate)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'responds to create_antibody' do
|
|
23
|
+
expect(client).to respond_to(:create_antibody)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'responds to scan_for_tactic' do
|
|
27
|
+
expect(client).to respond_to(:scan_for_tactic)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'responds to trigger_inflammatory_response' do
|
|
31
|
+
expect(client).to respond_to(:trigger_inflammatory_response)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'responds to resolve_inflammation' do
|
|
35
|
+
expect(client).to respond_to(:resolve_inflammation)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'responds to overall_immunity' do
|
|
39
|
+
expect(client).to respond_to(:overall_immunity)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'responds to vulnerability_report' do
|
|
43
|
+
expect(client).to respond_to(:vulnerability_report)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'responds to threat_history' do
|
|
47
|
+
expect(client).to respond_to(:threat_history)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'responds to decay_all' do
|
|
51
|
+
expect(client).to respond_to(:decay_all)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'responds to prune_ineffective' do
|
|
55
|
+
expect(client).to respond_to(:prune_ineffective)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'responds to immune_status' do
|
|
59
|
+
expect(client).to respond_to(:immune_status)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::CognitiveImmunology::Helpers::Antibody do
|
|
4
|
+
subject(:antibody) { described_class.new(tactic: :authority_appeal, pattern: 'claimed expert status') }
|
|
5
|
+
|
|
6
|
+
describe '#initialize' do
|
|
7
|
+
it 'assigns a uuid id' do
|
|
8
|
+
expect(antibody.id).to match(/\A[0-9a-f-]{36}\z/)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'stores tactic' do
|
|
12
|
+
expect(antibody.tactic).to eq(:authority_appeal)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'stores pattern' do
|
|
16
|
+
expect(antibody.pattern).to eq('claimed expert status')
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'defaults strength to 0.5' do
|
|
20
|
+
expect(antibody.strength).to eq(0.5)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'defaults matches to 0' do
|
|
24
|
+
expect(antibody.matches).to eq(0)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'clamps strength above 1.0' do
|
|
28
|
+
ab = described_class.new(tactic: :gaslighting, pattern: 'x', strength: 2.0)
|
|
29
|
+
expect(ab.strength).to eq(1.0)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'clamps strength below 0.0' do
|
|
33
|
+
ab = described_class.new(tactic: :gaslighting, pattern: 'x', strength: -1.0)
|
|
34
|
+
expect(ab.strength).to eq(0.0)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe '#match!' do
|
|
39
|
+
it 'increments matches' do
|
|
40
|
+
antibody.match!
|
|
41
|
+
expect(antibody.matches).to eq(1)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'boosts strength' do
|
|
45
|
+
original = antibody.strength
|
|
46
|
+
antibody.match!
|
|
47
|
+
expect(antibody.strength).to be > original
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'does not exceed 1.0' do
|
|
51
|
+
ab = described_class.new(tactic: :gaslighting, pattern: 'x', strength: 1.0)
|
|
52
|
+
ab.match!
|
|
53
|
+
expect(ab.strength).to eq(1.0)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe '#decay!' do
|
|
58
|
+
it 'reduces strength by RESISTANCE_DECAY' do
|
|
59
|
+
original = antibody.strength
|
|
60
|
+
antibody.decay!
|
|
61
|
+
expect(antibody.strength).to be_within(0.001).of(original - Legion::Extensions::CognitiveImmunology::Helpers::Constants::RESISTANCE_DECAY)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'does not go below 0.0' do
|
|
65
|
+
ab = described_class.new(tactic: :gaslighting, pattern: 'x', strength: 0.0)
|
|
66
|
+
ab.decay!
|
|
67
|
+
expect(ab.strength).to eq(0.0)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
describe '#effective?' do
|
|
72
|
+
it 'returns true when strength >= 0.3' do
|
|
73
|
+
expect(antibody.effective?).to be true
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'returns false when strength < 0.3' do
|
|
77
|
+
ab = described_class.new(tactic: :gaslighting, pattern: 'x', strength: 0.1)
|
|
78
|
+
expect(ab.effective?).to be false
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'returns true at exactly 0.3' do
|
|
82
|
+
ab = described_class.new(tactic: :gaslighting, pattern: 'x', strength: 0.3)
|
|
83
|
+
expect(ab.effective?).to be true
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
describe '#to_h' do
|
|
88
|
+
it 'returns a hash with all fields' do
|
|
89
|
+
h = antibody.to_h
|
|
90
|
+
expect(h).to include(:id, :tactic, :pattern, :strength, :matches, :effective, :created_at)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'includes effective status' do
|
|
94
|
+
h = antibody.to_h
|
|
95
|
+
expect(h[:effective]).to be true
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|