lex-cognitive-mirror 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a1a36459f44c549daf32dd96cdb4d65bf3d47819db378d55551686407f2da131
4
+ data.tar.gz: 34d942b917e9f0e32ff0a86e7eb1a69b0f68b2f6fbafa58c055690d176e23dc7
5
+ SHA512:
6
+ metadata.gz: e26065baf0ebbd17ccf4d2ebe2f9369f5d0dd84f8ba3607692205b3e86770b0ba501a7a23234246194870c02d7b1771bcf53bb4b81c02c7ec926dda500c91aac
7
+ data.tar.gz: c84f0a5825e192a6d93abf60647c2f5621f71f786bacf5e0d0d77f61c2e83643476103266b0f2ebb524f987091d142d60098bc1d57d0864516b1c81b01d5c48c
@@ -0,0 +1,16 @@
1
+ name: CI
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+
7
+ jobs:
8
+ ci:
9
+ uses: LegionIO/.github/.github/workflows/ci.yml@main
10
+
11
+ release:
12
+ needs: ci
13
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
14
+ uses: LegionIO/.github/.github/workflows/release.yml@main
15
+ secrets:
16
+ rubygems-api-key: ${{ secrets.RUBYGEMS_API_KEY }}
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --require spec_helper
2
+ --format documentation
3
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,44 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ TargetRubyVersion: 3.4
4
+ SuggestExtensions: false
5
+
6
+ Style/Documentation:
7
+ Enabled: false
8
+
9
+ Naming/PredicateMethod:
10
+ Enabled: false
11
+
12
+ Naming/PredicatePrefix:
13
+ Enabled: false
14
+
15
+ Metrics/ClassLength:
16
+ Max: 150
17
+
18
+ Metrics/MethodLength:
19
+ Max: 25
20
+
21
+ Metrics/AbcSize:
22
+ Max: 25
23
+
24
+ Metrics/ParameterLists:
25
+ Max: 8
26
+ MaxOptionalParameters: 8
27
+
28
+ Layout/HashAlignment:
29
+ EnforcedColonStyle: table
30
+ EnforcedHashRocketStyle: table
31
+
32
+ Metrics/BlockLength:
33
+ Exclude:
34
+ - 'spec/**/*'
35
+
36
+ Style/FrozenStringLiteralComment:
37
+ Enabled: true
38
+
39
+ Style/StringLiterals:
40
+ EnforcedStyle: single_quotes
41
+
42
+ Style/OneClassPerFile:
43
+ Exclude:
44
+ - 'spec/spec_helper.rb'
data/CLAUDE.md ADDED
@@ -0,0 +1,101 @@
1
+ # lex-cognitive-mirror
2
+
3
+ **Level 3 Leaf Documentation**
4
+ - **Parent**: `/Users/miverso2/rubymine/legion/extensions-agentic/CLAUDE.md`
5
+
6
+ ## Purpose
7
+
8
+ Theory of mind and empathic mirroring engine. Observes actions by other agents, simulates their likely intent, and tracks per-agent resonance scores (how aligned the agent is with the observer). Resonance boosts on each simulation and decays on each decay cycle. Accuracy tracking compares simulated outcomes against actual ones. Three separate runner modules split responsibilities: observation, simulation, and resonance management.
9
+
10
+ ## Gem Info
11
+
12
+ - **Gem name**: `lex-cognitive-mirror`
13
+ - **Module**: `Legion::Extensions::CognitiveMirror`
14
+ - **Version**: `0.1.0`
15
+ - **Ruby**: `>= 3.4`
16
+ - **License**: MIT
17
+
18
+ ## File Structure
19
+
20
+ ```
21
+ lib/legion/extensions/cognitive_mirror/
22
+ version.rb
23
+ client.rb
24
+ helpers/
25
+ constants.rb
26
+ mirror_engine.rb
27
+ runners/
28
+ observe.rb
29
+ simulate.rb
30
+ resonance.rb
31
+ ```
32
+
33
+ ## Key Constants
34
+
35
+ | Constant | Value | Purpose |
36
+ |---|---|---|
37
+ | `MAX_EVENTS` | `500` | Observation event ring buffer capacity |
38
+ | `MAX_SIMULATIONS` | `300` | Simulation record capacity |
39
+ | `DEFAULT_RESONANCE` | `0.5` | Starting resonance for new agents |
40
+ | `RESONANCE_BOOST` | `0.1` | Resonance increase per simulation |
41
+ | `RESONANCE_DECAY` | `0.03` | Per-cycle resonance decay |
42
+ | `ACTION_TYPES` | symbol array | Valid observable action types |
43
+ | `RESONANCE_LABELS` | range hash | From `:estranged` to `:deeply_resonant` |
44
+ | `CONFIDENCE_LABELS` | range hash | Simulation confidence labels |
45
+ | `EMPATHY_LABELS` | range hash | Empathy level labels |
46
+
47
+ ## Helpers
48
+
49
+ ### `Helpers::MirrorEngine`
50
+ Central engine with `INTENT_MAP` mapping action types to likely intents.
51
+
52
+ - `observe(agent_id:, action_type:, content:, context:)` → event record; appended to ring buffer
53
+ - `simulate(event_id:)` → derives outcome and intent via `INTENT_MAP`; boosts agent resonance by `RESONANCE_BOOST`
54
+ - `record_accuracy(simulation_id:, actual_outcome:)` → accuracy record for the simulation
55
+ - `empathic_resonance(agent_id:)` → current resonance float for agent
56
+ - `boost_resonance(agent_id:, amount:)` → directly boost resonance
57
+ - `decay_resonance(agent_id:)` → decay single agent's resonance by `RESONANCE_DECAY`
58
+ - `decay_all_resonances` → decay all known agents
59
+ - `simulation_accuracy_for(agent_id:)` → accuracy stats for agent
60
+ - `events_for(agent_id:)` → all observation events for agent
61
+ - `simulation_history(limit:)` → recent simulations
62
+ - `known_agents` → list of agent IDs with resonance scores
63
+
64
+ ## Runners
65
+
66
+ ### `Runners::Observe`
67
+ | Method | Description |
68
+ |---|---|
69
+ | `observe_action(agent_id:, action_type:, content:, context:)` | Record an observed action |
70
+ | `list_events(agent_id:, limit:)` | Events for an agent |
71
+
72
+ ### `Runners::Simulate`
73
+ | Method | Description |
74
+ |---|---|
75
+ | `simulate_action(event_id:)` | Simulate intent for an observed event |
76
+ | `record_simulation_accuracy(simulation_id:, actual_outcome:)` | Record actual outcome vs simulated |
77
+ | `simulation_history(limit:)` | Recent simulation records |
78
+
79
+ ### `Runners::Resonance`
80
+ | Method | Description |
81
+ |---|---|
82
+ | `empathic_resonance(agent_id:)` | Current resonance for agent |
83
+ | `decay_resonances(agent_id:)` | Decay one or all agent resonances |
84
+ | `resonance_summary` | All agents and their resonance scores |
85
+
86
+ All runners return `{success: true/false, ...}` hashes.
87
+
88
+ ## Integration Points
89
+
90
+ - `lex-mesh`: mirror observations are naturally triggered by mesh message receipt — observe the sending agent's action
91
+ - `lex-trust`: resonance scores complement trust scores; high resonance + high trust = most reliable agents
92
+ - `lex-tick` `mesh_interface` phase: observe all incoming mesh messages before processing
93
+ - `lex-identity`: behavioral fingerprint and empathic resonance are parallel models — fingerprint tracks the human partner, resonance tracks digital agents
94
+
95
+ ## Development Notes
96
+
97
+ - `Client` includes all three runner modules (`Observe`, `Simulate`, `Resonance`)
98
+ - `INTENT_MAP` is a static hash — intent inference is deterministic based on action type; no ML
99
+ - Resonance is bounded [0.0, 1.0] via clamping in `boost_resonance` and `decay_resonance`
100
+ - `MAX_EVENTS = 500` is a ring buffer; oldest events are dropped when full
101
+ - Simulation accuracy requires a follow-up `record_accuracy` call from the caller after the actual outcome is known
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
6
+
7
+ group :development, :test do
8
+ gem 'rspec', '~> 3.13'
9
+ gem 'rubocop', '~> 1.75'
10
+ gem 'rubocop-rspec'
11
+ end
12
+
13
+ gem 'legion-gaia', path: '../../legion-gaia'
data/Gemfile.lock ADDED
@@ -0,0 +1,78 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ lex-cognitive-mirror (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ addressable (2.8.9)
10
+ public_suffix (>= 2.0.2, < 8.0)
11
+ ast (2.4.3)
12
+ bigdecimal (4.0.1)
13
+ diff-lcs (1.6.2)
14
+ json (2.19.1)
15
+ json-schema (6.2.0)
16
+ addressable (~> 2.8)
17
+ bigdecimal (>= 3.1, < 5)
18
+ language_server-protocol (3.17.0.5)
19
+ lint_roller (1.1.0)
20
+ mcp (0.8.0)
21
+ json-schema (>= 4.1)
22
+ parallel (1.27.0)
23
+ parser (3.3.10.2)
24
+ ast (~> 2.4.1)
25
+ racc
26
+ prism (1.9.0)
27
+ public_suffix (7.0.5)
28
+ racc (1.8.1)
29
+ rainbow (3.1.1)
30
+ regexp_parser (2.11.3)
31
+ rspec (3.13.2)
32
+ rspec-core (~> 3.13.0)
33
+ rspec-expectations (~> 3.13.0)
34
+ rspec-mocks (~> 3.13.0)
35
+ rspec-core (3.13.6)
36
+ rspec-support (~> 3.13.0)
37
+ rspec-expectations (3.13.5)
38
+ diff-lcs (>= 1.2.0, < 2.0)
39
+ rspec-support (~> 3.13.0)
40
+ rspec-mocks (3.13.8)
41
+ diff-lcs (>= 1.2.0, < 2.0)
42
+ rspec-support (~> 3.13.0)
43
+ rspec-support (3.13.7)
44
+ rubocop (1.85.1)
45
+ json (~> 2.3)
46
+ language_server-protocol (~> 3.17.0.2)
47
+ lint_roller (~> 1.1.0)
48
+ mcp (~> 0.6)
49
+ parallel (~> 1.10)
50
+ parser (>= 3.3.0.2)
51
+ rainbow (>= 2.2.2, < 4.0)
52
+ regexp_parser (>= 2.9.3, < 3.0)
53
+ rubocop-ast (>= 1.49.0, < 2.0)
54
+ ruby-progressbar (~> 1.7)
55
+ unicode-display_width (>= 2.4.0, < 4.0)
56
+ rubocop-ast (1.49.1)
57
+ parser (>= 3.3.7.2)
58
+ prism (~> 1.7)
59
+ rubocop-rspec (3.9.0)
60
+ lint_roller (~> 1.1)
61
+ rubocop (~> 1.81)
62
+ ruby-progressbar (1.13.0)
63
+ unicode-display_width (3.2.0)
64
+ unicode-emoji (~> 4.1)
65
+ unicode-emoji (4.2.0)
66
+
67
+ PLATFORMS
68
+ arm64-darwin-25
69
+ ruby
70
+
71
+ DEPENDENCIES
72
+ lex-cognitive-mirror!
73
+ rspec (~> 3.13)
74
+ rubocop (~> 1.75)
75
+ rubocop-rspec
76
+
77
+ BUNDLED WITH
78
+ 2.6.9
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # lex-cognitive-mirror
2
+
3
+ Theory of mind and empathic mirroring for LegionIO cognitive agents. Observe other agents' actions, simulate their intent, track resonance scores, and measure simulation accuracy against actual outcomes.
4
+
5
+ ## What It Does
6
+
7
+ - Observe actions from other agents (record into event ring buffer)
8
+ - Simulate likely intent for observed events using an intent map
9
+ - Each simulation boosts resonance with that agent
10
+ - Resonance scores decay over time
11
+ - Record actual outcomes to measure simulation accuracy
12
+ - Track per-agent resonance, confidence, and empathy labels
13
+ - Three focused runner modules: `Observe`, `Simulate`, `Resonance`
14
+
15
+ ## Usage
16
+
17
+ ```ruby
18
+ # Observe an action
19
+ runner.observe_action(agent_id: 'agent-123', action_type: :propose,
20
+ content: 'suggesting a new approach', context: { domain: :architecture })
21
+ # => { success: true, event_id: '...', agent_id: 'agent-123' }
22
+
23
+ # Simulate intent for the event
24
+ runner.simulate_action(event_id: event_id)
25
+ # => { success: true, simulation_id: '...', simulated_intent: :collaboration, confidence: 0.7, ... }
26
+
27
+ # Record actual outcome to track accuracy
28
+ runner.record_simulation_accuracy(simulation_id: sim_id, actual_outcome: :collaboration)
29
+
30
+ # Check resonance
31
+ runner.empathic_resonance(agent_id: 'agent-123')
32
+ # => { success: true, agent_id: 'agent-123', resonance: 0.6, label: :resonant }
33
+
34
+ # Decay all resonances (call each tick)
35
+ runner.decay_resonances(agent_id: nil)
36
+
37
+ # Summary of all known agents
38
+ runner.resonance_summary
39
+ ```
40
+
41
+ ## Development
42
+
43
+ ```bash
44
+ bundle install
45
+ bundle exec rspec
46
+ bundle exec rubocop
47
+ ```
48
+
49
+ ## License
50
+
51
+ MIT
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/legion/extensions/cognitive_mirror/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'lex-cognitive-mirror'
7
+ spec.version = Legion::Extensions::CognitiveMirror::VERSION
8
+ spec.authors = ['Esity']
9
+ spec.email = ['matthewdiverson@gmail.com']
10
+
11
+ spec.summary = 'Mirror neuron simulation for LegionIO agents'
12
+ spec.description = 'Mirror neuron simulation for LegionIO: observe, simulate, and build empathic resonance'
13
+ spec.homepage = 'https://github.com/LegionIO/lex-cognitive-mirror'
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'] = spec.homepage
19
+ spec.metadata['documentation_uri'] = "#{spec.homepage}/blob/main/README.md"
20
+ spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/main/CHANGELOG.md"
21
+ spec.metadata['bug_tracker_uri'] = "#{spec.homepage}/issues"
22
+ spec.metadata['rubygems_mfa_required'] = 'true'
23
+
24
+ spec.files = Dir.chdir(__dir__) do
25
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
26
+ end
27
+ spec.require_paths = ['lib']
28
+ spec.add_development_dependency 'legion-gaia'
29
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/cognitive_mirror/helpers/constants'
4
+ require 'legion/extensions/cognitive_mirror/helpers/mirror_event'
5
+ require 'legion/extensions/cognitive_mirror/helpers/simulation'
6
+ require 'legion/extensions/cognitive_mirror/helpers/mirror_engine'
7
+ require 'legion/extensions/cognitive_mirror/runners/observe'
8
+ require 'legion/extensions/cognitive_mirror/runners/simulate'
9
+ require 'legion/extensions/cognitive_mirror/runners/resonance'
10
+
11
+ module Legion
12
+ module Extensions
13
+ module CognitiveMirror
14
+ class Client
15
+ include Runners::Observe
16
+ include Runners::Simulate
17
+ include Runners::Resonance
18
+
19
+ def initialize(**)
20
+ @mirror_engine = Helpers::MirrorEngine.new
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :mirror_engine
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveMirror
6
+ module Helpers
7
+ module Constants
8
+ MAX_EVENTS = 500
9
+ MAX_SIMULATIONS = 300
10
+ DEFAULT_RESONANCE = 0.5
11
+ RESONANCE_BOOST = 0.1
12
+ RESONANCE_DECAY = 0.03
13
+ SIMULATION_CONFIDENCE_DEFAULT = 0.5
14
+
15
+ ACTION_TYPES = %i[
16
+ movement
17
+ communication
18
+ decision
19
+ emotional_expression
20
+ creative_act
21
+ analytical_task
22
+ social_interaction
23
+ unknown
24
+ ].freeze
25
+
26
+ RESONANCE_LABELS = {
27
+ (0.0..0.2) => :minimal,
28
+ (0.2..0.4) => :low,
29
+ (0.4..0.6) => :moderate,
30
+ (0.6..0.8) => :high,
31
+ (0.8..1.0) => :deep
32
+ }.freeze
33
+
34
+ CONFIDENCE_LABELS = {
35
+ (0.0..0.2) => :uncertain,
36
+ (0.2..0.4) => :tentative,
37
+ (0.4..0.6) => :plausible,
38
+ (0.6..0.8) => :confident,
39
+ (0.8..1.0) => :certain
40
+ }.freeze
41
+
42
+ EMPATHY_LABELS = {
43
+ (0.0..0.2) => :detached,
44
+ (0.2..0.4) => :aware,
45
+ (0.4..0.6) => :attuned,
46
+ (0.6..0.8) => :resonant,
47
+ (0.8..1.0) => :immersed
48
+ }.freeze
49
+
50
+ def self.label_for(labels_hash, value)
51
+ clamped = value.clamp(0.0, 1.0)
52
+ labels_hash.each do |range, label|
53
+ return label if range.cover?(clamped)
54
+ end
55
+ labels_hash.values.last
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveMirror
6
+ module Helpers
7
+ class MirrorEngine
8
+ INTENT_MAP = {
9
+ communication: :inform_or_persuade,
10
+ decision: :resolve_uncertainty,
11
+ emotional_expression: :signal_internal_state,
12
+ creative_act: :generate_novelty,
13
+ analytical_task: :reduce_uncertainty,
14
+ social_interaction: :build_relationship,
15
+ movement: :change_position
16
+ }.freeze
17
+
18
+ attr_reader :events, :simulations, :resonance_map
19
+
20
+ def initialize
21
+ @events = []
22
+ @simulations = []
23
+ @resonance_map = Hash.new(Constants::DEFAULT_RESONANCE)
24
+ end
25
+
26
+ def observe(agent_id:, action_type:, context: {}, emotional_valence: 0.0)
27
+ event = MirrorEvent.new(
28
+ agent_id: agent_id,
29
+ action_type: action_type,
30
+ context: context,
31
+ emotional_valence: emotional_valence
32
+ )
33
+ @events << event
34
+ @events.shift while @events.size > Constants::MAX_EVENTS
35
+ event
36
+ end
37
+
38
+ def simulate(event, confidence: Constants::SIMULATION_CONFIDENCE_DEFAULT)
39
+ resonance = compute_resonance_for(event)
40
+ outcome = derive_outcome(event)
41
+
42
+ sim = Simulation.new(
43
+ event_id: event.id,
44
+ simulated_outcome: outcome,
45
+ confidence: confidence,
46
+ emotional_resonance: resonance
47
+ )
48
+ @simulations << sim
49
+ @simulations.shift while @simulations.size > Constants::MAX_SIMULATIONS
50
+ sim
51
+ end
52
+
53
+ def record_accuracy(simulation_id, accuracy)
54
+ sim = @simulations.find { |s| s.id == simulation_id }
55
+ return false unless sim
56
+
57
+ sim.accuracy_score = accuracy.to_f.clamp(0.0, 1.0)
58
+ true
59
+ end
60
+
61
+ def empathic_resonance(agent_id)
62
+ @resonance_map[agent_id].clamp(0.0, 1.0)
63
+ end
64
+
65
+ def boost_resonance(agent_id)
66
+ current = @resonance_map[agent_id]
67
+ @resonance_map[agent_id] = (current + Constants::RESONANCE_BOOST).round(10).clamp(0.0, 1.0)
68
+ end
69
+
70
+ def decay_resonance(agent_id)
71
+ current = @resonance_map[agent_id]
72
+ @resonance_map[agent_id] = (current - Constants::RESONANCE_DECAY).round(10).clamp(0.0, 1.0)
73
+ end
74
+
75
+ def decay_all_resonances
76
+ @resonance_map.each_key { |agent_id| decay_resonance(agent_id) }
77
+ end
78
+
79
+ def simulation_accuracy_for(agent_id)
80
+ scored = simulations_for(agent_id).reject { |s| s.accuracy_score.nil? }
81
+ return nil if scored.empty?
82
+
83
+ scored.sum(&:accuracy_score) / scored.size.to_f
84
+ end
85
+
86
+ def simulations_for(agent_id)
87
+ event_ids = events_for(agent_id).map(&:id)
88
+ @simulations.select { |s| event_ids.include?(s.event_id) }
89
+ end
90
+
91
+ def events_for(agent_id)
92
+ @events.select { |e| e.agent_id == agent_id }
93
+ end
94
+
95
+ def simulation_history(limit: 20)
96
+ @simulations.last(limit).map(&:to_h)
97
+ end
98
+
99
+ def known_agents
100
+ @resonance_map.keys
101
+ end
102
+
103
+ private
104
+
105
+ def compute_resonance_for(event)
106
+ base = @resonance_map[event.agent_id]
107
+ valence = event.emotional_valence.abs
108
+ resonance = (base + (valence * Constants::RESONANCE_BOOST)).round(10)
109
+ resonance.clamp(0.0, 1.0)
110
+ end
111
+
112
+ def derive_outcome(event)
113
+ {
114
+ action_type: event.action_type,
115
+ predicted_intent: infer_intent(event),
116
+ emotional_echo: event.emotional_valence,
117
+ context_keys: event.context.keys
118
+ }
119
+ end
120
+
121
+ def infer_intent(event)
122
+ INTENT_MAP.fetch(event.action_type, :unknown_intent)
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module CognitiveMirror
8
+ module Helpers
9
+ class MirrorEvent
10
+ attr_reader :id, :agent_id, :action_type, :context, :emotional_valence, :observed_at
11
+
12
+ def initialize(agent_id:, action_type:, context: {}, emotional_valence: 0.0)
13
+ @id = SecureRandom.uuid
14
+ @agent_id = agent_id
15
+ @action_type = normalize_action_type(action_type)
16
+ @context = context.is_a?(Hash) ? context : {}
17
+ @emotional_valence = emotional_valence.to_f.clamp(-1.0, 1.0)
18
+ @observed_at = Time.now.utc
19
+ end
20
+
21
+ def to_h
22
+ {
23
+ id: @id,
24
+ agent_id: @agent_id,
25
+ action_type: @action_type,
26
+ context: @context,
27
+ emotional_valence: @emotional_valence,
28
+ observed_at: @observed_at
29
+ }
30
+ end
31
+
32
+ private
33
+
34
+ def normalize_action_type(action_type)
35
+ sym = action_type.to_sym
36
+ Constants::ACTION_TYPES.include?(sym) ? sym : :unknown
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module CognitiveMirror
8
+ module Helpers
9
+ class Simulation
10
+ attr_reader :id, :event_id, :simulated_outcome, :confidence, :emotional_resonance, :simulated_at
11
+ attr_accessor :accuracy_score
12
+
13
+ def initialize(event_id:, simulated_outcome:, confidence: Constants::SIMULATION_CONFIDENCE_DEFAULT,
14
+ emotional_resonance: Constants::DEFAULT_RESONANCE)
15
+ @id = SecureRandom.uuid
16
+ @event_id = event_id
17
+ @simulated_outcome = simulated_outcome
18
+ @confidence = confidence.to_f.clamp(0.0, 1.0)
19
+ @emotional_resonance = emotional_resonance.to_f.clamp(0.0, 1.0)
20
+ @simulated_at = Time.now.utc
21
+ @accuracy_score = nil
22
+ end
23
+
24
+ def to_h
25
+ {
26
+ id: @id,
27
+ event_id: @event_id,
28
+ simulated_outcome: @simulated_outcome,
29
+ confidence: @confidence,
30
+ emotional_resonance: @emotional_resonance,
31
+ simulated_at: @simulated_at,
32
+ accuracy_score: @accuracy_score
33
+ }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveMirror
6
+ module Runners
7
+ module Observe
8
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
9
+ Legion::Extensions::Helpers.const_defined?(:Lex)
10
+
11
+ def observe_action(agent_id:, action_type:, context: {}, emotional_valence: 0.0, engine: nil, **)
12
+ eng = engine || mirror_engine
13
+
14
+ unless Helpers::Constants::ACTION_TYPES.include?(action_type.to_sym)
15
+ Legion::Logging.debug "[cognitive_mirror] unknown action_type=#{action_type}, mapping to :unknown"
16
+ end
17
+
18
+ event = eng.observe(
19
+ agent_id: agent_id,
20
+ action_type: action_type,
21
+ context: context,
22
+ emotional_valence: emotional_valence
23
+ )
24
+
25
+ resonance_label = Helpers::Constants.label_for(Helpers::Constants::RESONANCE_LABELS,
26
+ eng.empathic_resonance(agent_id))
27
+ Legion::Logging.debug "[cognitive_mirror] observed action=#{event.action_type} " \
28
+ "agent=#{agent_id} resonance_tier=#{resonance_label}"
29
+
30
+ { success: true, event: event.to_h, resonance_tier: resonance_label }
31
+ end
32
+
33
+ def list_events(agent_id: nil, engine: nil, **)
34
+ eng = engine || mirror_engine
35
+ events = agent_id ? eng.events_for(agent_id) : eng.events
36
+ { success: true, events: events.map(&:to_h), count: events.size }
37
+ end
38
+
39
+ private
40
+
41
+ def mirror_engine
42
+ @mirror_engine ||= Helpers::MirrorEngine.new
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveMirror
6
+ module Runners
7
+ module Resonance
8
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
9
+ Legion::Extensions::Helpers.const_defined?(:Lex)
10
+
11
+ def empathic_resonance(agent_id:, engine: nil, **)
12
+ eng = engine || mirror_engine
13
+ value = eng.empathic_resonance(agent_id)
14
+ accuracy = eng.simulation_accuracy_for(agent_id)
15
+
16
+ empathy_label = Helpers::Constants.label_for(Helpers::Constants::EMPATHY_LABELS, value)
17
+ resonance_label = Helpers::Constants.label_for(Helpers::Constants::RESONANCE_LABELS, value)
18
+
19
+ Legion::Logging.debug "[cognitive_mirror] resonance agent=#{agent_id} " \
20
+ "value=#{value.round(3)} empathy=#{empathy_label}"
21
+
22
+ {
23
+ success: true,
24
+ agent_id: agent_id,
25
+ resonance: value,
26
+ resonance_label: resonance_label,
27
+ empathy_label: empathy_label,
28
+ accuracy: accuracy,
29
+ event_count: eng.events_for(agent_id).size,
30
+ simulation_count: eng.simulations_for(agent_id).size
31
+ }
32
+ end
33
+
34
+ def decay_resonances(agent_id: nil, engine: nil, **)
35
+ eng = engine || mirror_engine
36
+
37
+ if agent_id
38
+ eng.decay_resonance(agent_id)
39
+ after = eng.empathic_resonance(agent_id)
40
+ Legion::Logging.debug "[cognitive_mirror] resonance decayed agent=#{agent_id} after=#{after.round(3)}"
41
+ { success: true, agent_id: agent_id, resonance_after: after }
42
+ else
43
+ eng.decay_all_resonances
44
+ summary = eng.known_agents.to_h { |id| [id, eng.empathic_resonance(id)] }
45
+ Legion::Logging.debug "[cognitive_mirror] global resonance decay agents=#{summary.size}"
46
+ { success: true, agents_decayed: summary.size, resonances: summary }
47
+ end
48
+ end
49
+
50
+ def resonance_summary(engine: nil, **)
51
+ eng = engine || mirror_engine
52
+ agents = eng.known_agents
53
+
54
+ summary = agents.map do |id|
55
+ value = eng.empathic_resonance(id)
56
+ {
57
+ agent_id: id,
58
+ resonance: value,
59
+ empathy_label: Helpers::Constants.label_for(Helpers::Constants::EMPATHY_LABELS, value)
60
+ }
61
+ end
62
+
63
+ { success: true, agents: summary, total: agents.size }
64
+ end
65
+
66
+ private
67
+
68
+ def mirror_engine
69
+ @mirror_engine ||= Helpers::MirrorEngine.new
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveMirror
6
+ module Runners
7
+ module Simulate
8
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
9
+ Legion::Extensions::Helpers.const_defined?(:Lex)
10
+
11
+ def simulate_action(event_id:, confidence: Helpers::Constants::SIMULATION_CONFIDENCE_DEFAULT,
12
+ engine: nil, **)
13
+ eng = engine || mirror_engine
14
+
15
+ event = eng.events.find { |e| e.id == event_id }
16
+ unless event
17
+ Legion::Logging.debug "[cognitive_mirror] simulate_action: event_id=#{event_id} not found"
18
+ return { success: false, error: 'event not found', event_id: event_id }
19
+ end
20
+
21
+ sim = eng.simulate(event, confidence: confidence)
22
+ eng.boost_resonance(event.agent_id)
23
+
24
+ confidence_label = Helpers::Constants.label_for(Helpers::Constants::CONFIDENCE_LABELS, sim.confidence)
25
+ Legion::Logging.debug "[cognitive_mirror] simulated event=#{event_id} " \
26
+ "confidence_tier=#{confidence_label} resonance=#{sim.emotional_resonance.round(3)}"
27
+
28
+ { success: true, simulation: sim.to_h, confidence_tier: confidence_label }
29
+ end
30
+
31
+ def record_simulation_accuracy(simulation_id:, accuracy:, engine: nil, **)
32
+ eng = engine || mirror_engine
33
+ recorded = eng.record_accuracy(simulation_id, accuracy)
34
+
35
+ unless recorded
36
+ Legion::Logging.debug "[cognitive_mirror] record_accuracy: simulation_id=#{simulation_id} not found"
37
+ return { success: false, error: 'simulation not found', simulation_id: simulation_id }
38
+ end
39
+
40
+ Legion::Logging.debug "[cognitive_mirror] accuracy recorded simulation=#{simulation_id} score=#{accuracy}"
41
+ { success: true, simulation_id: simulation_id, accuracy: accuracy.to_f.clamp(0.0, 1.0) }
42
+ end
43
+
44
+ def simulation_history(limit: 20, engine: nil, **)
45
+ eng = engine || mirror_engine
46
+ history = eng.simulation_history(limit: limit)
47
+ { success: true, simulations: history, count: history.size }
48
+ end
49
+
50
+ private
51
+
52
+ def mirror_engine
53
+ @mirror_engine ||= Helpers::MirrorEngine.new
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveMirror
6
+ VERSION = '0.1.0'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/cognitive_mirror/version'
4
+ require 'legion/extensions/cognitive_mirror/helpers/constants'
5
+ require 'legion/extensions/cognitive_mirror/helpers/mirror_event'
6
+ require 'legion/extensions/cognitive_mirror/helpers/simulation'
7
+ require 'legion/extensions/cognitive_mirror/helpers/mirror_engine'
8
+ require 'legion/extensions/cognitive_mirror/runners/observe'
9
+ require 'legion/extensions/cognitive_mirror/runners/simulate'
10
+ require 'legion/extensions/cognitive_mirror/runners/resonance'
11
+ require 'legion/extensions/cognitive_mirror/client'
12
+
13
+ module Legion
14
+ module Extensions
15
+ module CognitiveMirror
16
+ extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
17
+ end
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lex-cognitive-mirror
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Esity
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: legion-gaia
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ description: 'Mirror neuron simulation for LegionIO: observe, simulate, and build
27
+ empathic resonance'
28
+ email:
29
+ - matthewdiverson@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".github/workflows/ci.yml"
35
+ - ".gitignore"
36
+ - ".rspec"
37
+ - ".rubocop.yml"
38
+ - CLAUDE.md
39
+ - Gemfile
40
+ - Gemfile.lock
41
+ - README.md
42
+ - lex-cognitive-mirror.gemspec
43
+ - lib/legion/extensions/cognitive_mirror.rb
44
+ - lib/legion/extensions/cognitive_mirror/client.rb
45
+ - lib/legion/extensions/cognitive_mirror/helpers/constants.rb
46
+ - lib/legion/extensions/cognitive_mirror/helpers/mirror_engine.rb
47
+ - lib/legion/extensions/cognitive_mirror/helpers/mirror_event.rb
48
+ - lib/legion/extensions/cognitive_mirror/helpers/simulation.rb
49
+ - lib/legion/extensions/cognitive_mirror/runners/observe.rb
50
+ - lib/legion/extensions/cognitive_mirror/runners/resonance.rb
51
+ - lib/legion/extensions/cognitive_mirror/runners/simulate.rb
52
+ - lib/legion/extensions/cognitive_mirror/version.rb
53
+ homepage: https://github.com/LegionIO/lex-cognitive-mirror
54
+ licenses:
55
+ - MIT
56
+ metadata:
57
+ homepage_uri: https://github.com/LegionIO/lex-cognitive-mirror
58
+ source_code_uri: https://github.com/LegionIO/lex-cognitive-mirror
59
+ documentation_uri: https://github.com/LegionIO/lex-cognitive-mirror/blob/main/README.md
60
+ changelog_uri: https://github.com/LegionIO/lex-cognitive-mirror/blob/main/CHANGELOG.md
61
+ bug_tracker_uri: https://github.com/LegionIO/lex-cognitive-mirror/issues
62
+ rubygems_mfa_required: 'true'
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '3.4'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubygems_version: 3.6.9
78
+ specification_version: 4
79
+ summary: Mirror neuron simulation for LegionIO agents
80
+ test_files: []