lex-cognitive-phantom 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/.github/workflows/ci.yml +16 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.rubocop.yml +64 -0
- data/CLAUDE.md +86 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +78 -0
- data/README.md +53 -0
- data/lex-cognitive-phantom.gemspec +27 -0
- data/lib/legion/extensions/cognitive_phantom/client.rb +25 -0
- data/lib/legion/extensions/cognitive_phantom/helpers/constants.rb +50 -0
- data/lib/legion/extensions/cognitive_phantom/helpers/phantom_engine.rb +102 -0
- data/lib/legion/extensions/cognitive_phantom/helpers/phantom_limb.rb +99 -0
- data/lib/legion/extensions/cognitive_phantom/helpers/phantom_signal.rb +36 -0
- data/lib/legion/extensions/cognitive_phantom/runners/cognitive_phantom.rb +75 -0
- data/lib/legion/extensions/cognitive_phantom/version.rb +9 -0
- data/lib/legion/extensions/cognitive_phantom.rb +17 -0
- metadata +78 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 6b3ee604853dcc9f14a6d0d0d97fd23f8766ece92475d5abc06257139552b22c
|
|
4
|
+
data.tar.gz: 87bcb8aa7479fa4155c610e3969fdb667cee1b2f9962c3a7fd6e1f8c82ea69c7
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 61f7abd1e3575004f8f7d3153b8b9838159b0662bc10ab6f9343876edcbd514cc5d6efd4685d428b17fe2beb2a61d9092fb02acb49364e6faccddf02d9258a51
|
|
7
|
+
data.tar.gz: e2356291933cea4b484e8b7fa8f239cd53d5bf7f7f0714c5c945d3dfa5eefbae24509ed12a60e0de306b6f4d46014c09e73d891239d7b5da18b525ea8dd765ee
|
|
@@ -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
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 3.4
|
|
3
|
+
NewCops: enable
|
|
4
|
+
SuggestExtensions: false
|
|
5
|
+
|
|
6
|
+
Layout/LineLength:
|
|
7
|
+
Max: 160
|
|
8
|
+
|
|
9
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
|
10
|
+
EnforcedStyle: space
|
|
11
|
+
|
|
12
|
+
Layout/HashAlignment:
|
|
13
|
+
EnforcedHashRocketStyle: table
|
|
14
|
+
EnforcedColonStyle: table
|
|
15
|
+
|
|
16
|
+
Metrics/MethodLength:
|
|
17
|
+
Max: 25
|
|
18
|
+
|
|
19
|
+
Metrics/ClassLength:
|
|
20
|
+
Max: 150
|
|
21
|
+
|
|
22
|
+
Metrics/ModuleLength:
|
|
23
|
+
Max: 1500
|
|
24
|
+
|
|
25
|
+
Metrics/BlockLength:
|
|
26
|
+
Max: 40
|
|
27
|
+
Exclude:
|
|
28
|
+
- 'spec/**/*'
|
|
29
|
+
|
|
30
|
+
Metrics/AbcSize:
|
|
31
|
+
Max: 25
|
|
32
|
+
|
|
33
|
+
Metrics/CyclomaticComplexity:
|
|
34
|
+
Max: 15
|
|
35
|
+
|
|
36
|
+
Metrics/PerceivedComplexity:
|
|
37
|
+
Max: 17
|
|
38
|
+
|
|
39
|
+
Metrics/ParameterLists:
|
|
40
|
+
Max: 8
|
|
41
|
+
MaxOptionalParameters: 8
|
|
42
|
+
|
|
43
|
+
Style/Documentation:
|
|
44
|
+
Enabled: false
|
|
45
|
+
|
|
46
|
+
Style/OneClassPerFile:
|
|
47
|
+
Exclude:
|
|
48
|
+
- 'spec/spec_helper.rb'
|
|
49
|
+
|
|
50
|
+
Style/SymbolArray:
|
|
51
|
+
Enabled: true
|
|
52
|
+
|
|
53
|
+
Style/FrozenStringLiteralComment:
|
|
54
|
+
Enabled: true
|
|
55
|
+
EnforcedStyle: always
|
|
56
|
+
|
|
57
|
+
Naming/FileName:
|
|
58
|
+
Enabled: false
|
|
59
|
+
|
|
60
|
+
Naming/PredicateMethod:
|
|
61
|
+
Enabled: false
|
|
62
|
+
|
|
63
|
+
Naming/PredicatePrefix:
|
|
64
|
+
Enabled: false
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# lex-cognitive-phantom
|
|
2
|
+
|
|
3
|
+
**Level 3 Leaf Documentation**
|
|
4
|
+
- **Parent**: `/Users/miverso2/rubymine/legion/extensions-agentic/CLAUDE.md`
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
|
|
8
|
+
Phantom limb metaphor for removed cognitive capabilities. When a capability (tool, permission, access, skill, memory, context, etc.) is removed from the agent, a `PhantomLimb` is created representing the lingering ghost signal. Intensity decays over time; incoming stimuli that match the phantom's capability type trigger the phantom and slightly boost intensity. States progress from `acute` → `adapting` → `residual` → `resolved` as intensity decays.
|
|
9
|
+
|
|
10
|
+
## Gem Info
|
|
11
|
+
|
|
12
|
+
- **Gem name**: `lex-cognitive-phantom`
|
|
13
|
+
- **Module**: `Legion::Extensions::CognitivePhantom`
|
|
14
|
+
- **Version**: `0.1.0`
|
|
15
|
+
- **Ruby**: `>= 3.4`
|
|
16
|
+
- **License**: MIT
|
|
17
|
+
|
|
18
|
+
## File Structure
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
lib/legion/extensions/cognitive_phantom/
|
|
22
|
+
version.rb
|
|
23
|
+
client.rb
|
|
24
|
+
helpers/
|
|
25
|
+
constants.rb
|
|
26
|
+
phantom_limb.rb
|
|
27
|
+
runners/
|
|
28
|
+
cognitive_phantom.rb
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Key Constants
|
|
32
|
+
|
|
33
|
+
| Constant | Value | Purpose |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `MAX_PHANTOMS` | `100` | Per-engine phantom capacity |
|
|
36
|
+
| `INITIAL_INTENSITY` | `0.8` | Intensity when a phantom is first created |
|
|
37
|
+
| `INTENSITY_DECAY` | `0.05` | Per-cycle intensity decay |
|
|
38
|
+
| `MIN_INTENSITY` | `0.01` | Below this intensity, phantom is resolved |
|
|
39
|
+
| `PHANTOM_STATES` | `%i[acute adapting residual resolved]` | Lifecycle states |
|
|
40
|
+
| `TRIGGER_TYPES` | symbol array | Valid trigger categories (e.g., `:stimulus_match`, `:context_reminder`) |
|
|
41
|
+
| `STATE_THRESHOLDS` | hash | Intensity thresholds per state |
|
|
42
|
+
| `PHANTOM_LABELS` | range hash | Phantom intensity labels |
|
|
43
|
+
|
|
44
|
+
## Helpers
|
|
45
|
+
|
|
46
|
+
### `Helpers::PhantomLimb`
|
|
47
|
+
Individual phantom record. Has `id`, `capability_type`, `domain`, `content`, `intensity`, `signals` (array of `PhantomSignal`), `trigger_count`, and `resolved_at`.
|
|
48
|
+
|
|
49
|
+
- `trigger!(stimulus)` — classifies trigger type from stimulus, boosts intensity by 5% of remaining headroom, creates `PhantomSignal` record
|
|
50
|
+
- `decay!` — reduces intensity by `INTENSITY_DECAY`; sets `resolved_at` if intensity drops to `MIN_INTENSITY`
|
|
51
|
+
- `adapt!` — applies 2.5× decay rate (accelerated adaptation)
|
|
52
|
+
- `resolved?` — intensity <= `MIN_INTENSITY`
|
|
53
|
+
- `state` — returns `:acute` (>= 0.6), `:adapting` (>= 0.3), `:residual` (> MIN), or `:resolved`
|
|
54
|
+
- `intensity` — current signal intensity
|
|
55
|
+
|
|
56
|
+
### `PhantomSignal` (inline value object)
|
|
57
|
+
Immutable record of a trigger event: `trigger_type`, `stimulus`, and `occurred_at`.
|
|
58
|
+
|
|
59
|
+
## Runners
|
|
60
|
+
|
|
61
|
+
Module: `Runners::CognitivePhantom`
|
|
62
|
+
|
|
63
|
+
| Runner Method | Description |
|
|
64
|
+
|---|---|
|
|
65
|
+
| `register_removal(capability_type:, domain:, content:)` | Register a removed capability as a phantom |
|
|
66
|
+
| `process_stimulus(phantom_id:, stimulus:)` | Process incoming stimulus against phantom |
|
|
67
|
+
| `acknowledge_phantom(phantom_id:)` | Apply accelerated adaptation (adapt!) |
|
|
68
|
+
| `phantom_status` | Aggregate phantom report |
|
|
69
|
+
| `decay_all` | Apply standard decay to all phantoms; prune resolved ones |
|
|
70
|
+
|
|
71
|
+
All runners return `{success: true/false, ...}` hashes.
|
|
72
|
+
|
|
73
|
+
## Integration Points
|
|
74
|
+
|
|
75
|
+
- `lex-extinction` level 1/2 operations: when capabilities are removed by an extinction protocol, register them as phantoms
|
|
76
|
+
- `lex-tick` `action_selection`: check for triggered phantoms before executing actions that require the removed capability
|
|
77
|
+
- `lex-emotion`: acute phantoms contribute negative valence; resolved phantoms are a positive signal
|
|
78
|
+
- `lex-identity`: phantom signals reflect behavioral pattern disruption — high trigger count phantoms affect behavioral fingerprint
|
|
79
|
+
|
|
80
|
+
## Development Notes
|
|
81
|
+
|
|
82
|
+
- `Client` instantiates `@phantom_engine = Helpers::PhantomEngine.new`
|
|
83
|
+
- `trigger!` boost is small (5% of headroom) to prevent re-escalation to acute state from residual
|
|
84
|
+
- `adapt!` applies 2.5× decay — allows the agent to explicitly accelerate acceptance of capability loss
|
|
85
|
+
- `decay_all` removes resolved phantoms from memory (garbage collection)
|
|
86
|
+
- `INITIAL_INTENSITY = 0.8` models the immediate strong signal when something important is removed; starting intensity below `PHANTOM_STATES` `:acute` threshold (0.6) would bypass the acute state
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
lex-cognitive-phantom (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-phantom!
|
|
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,53 @@
|
|
|
1
|
+
# lex-cognitive-phantom
|
|
2
|
+
|
|
3
|
+
Phantom limb model for removed cognitive capabilities in LegionIO agents. When a capability is removed, a phantom is registered with high initial intensity. Intensity decays over time; matching stimuli trigger the phantom; acknowledging it accelerates adaptation.
|
|
4
|
+
|
|
5
|
+
## What It Does
|
|
6
|
+
|
|
7
|
+
- Register removed capabilities (tool, permission, memory, context, skill, etc.) as phantoms
|
|
8
|
+
- Initial intensity: 0.8 (acute state)
|
|
9
|
+
- Four states: acute (>= 0.6) → adapting (>= 0.3) → residual (> 0.01) → resolved
|
|
10
|
+
- Incoming stimuli trigger the phantom (small intensity boost + signal record)
|
|
11
|
+
- `acknowledge_phantom` applies 2.5× decay for accelerated adaptation
|
|
12
|
+
- Standard decay (INTENSITY_DECAY = 0.05/cycle) runs via `decay_all`
|
|
13
|
+
- Resolved phantoms are pruned from memory automatically
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
# Register a removed capability
|
|
19
|
+
result = runner.register_removal(
|
|
20
|
+
capability_type: :tool_access, domain: :operations,
|
|
21
|
+
content: 'database write permission removed by governance'
|
|
22
|
+
)
|
|
23
|
+
phantom_id = result[:phantom][:id]
|
|
24
|
+
|
|
25
|
+
# Process a stimulus that touches the removed capability
|
|
26
|
+
runner.process_stimulus(phantom_id: phantom_id,
|
|
27
|
+
stimulus: 'attempted to write to DB')
|
|
28
|
+
# => { success: true, triggered: true, intensity: 0.81, trigger_type: :stimulus_match, state: :acute }
|
|
29
|
+
|
|
30
|
+
# Acknowledge to accelerate adaptation
|
|
31
|
+
runner.acknowledge_phantom(phantom_id: phantom_id)
|
|
32
|
+
# => { success: true, phantom: { intensity: 0.68, state: :adapting } }
|
|
33
|
+
|
|
34
|
+
# Apply standard decay each tick
|
|
35
|
+
runner.decay_all
|
|
36
|
+
# => { success: true, decayed: 1, resolved: 0 }
|
|
37
|
+
|
|
38
|
+
# Overall status
|
|
39
|
+
runner.phantom_status
|
|
40
|
+
# => { success: true, total: 1, acute: 0, adapting: 1, residual: 0, resolved: 0 }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Development
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
bundle install
|
|
47
|
+
bundle exec rspec
|
|
48
|
+
bundle exec rubocop
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## License
|
|
52
|
+
|
|
53
|
+
MIT
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/cognitive_phantom/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lex-cognitive-phantom'
|
|
7
|
+
spec.version = Legion::Extensions::CognitivePhantom::VERSION
|
|
8
|
+
spec.authors = ['Esity']
|
|
9
|
+
spec.email = ['matthewdiverson@gmail.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'LEX Cognitive Phantom'
|
|
12
|
+
spec.description = 'Phantom limb model for AI: ghost signals from removed capabilities fade over time as the agent adapts'
|
|
13
|
+
spec.homepage = 'https://github.com/LegionIO/lex-cognitive-phantom'
|
|
14
|
+
spec.license = 'MIT'
|
|
15
|
+
spec.required_ruby_version = '>= 3.4'
|
|
16
|
+
|
|
17
|
+
spec.metadata['homepage_uri'] = 'https://github.com/LegionIO/lex-cognitive-phantom'
|
|
18
|
+
spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-cognitive-phantom'
|
|
19
|
+
spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-cognitive-phantom'
|
|
20
|
+
spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-cognitive-phantom'
|
|
21
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-cognitive-phantom/issues'
|
|
22
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
23
|
+
|
|
24
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
|
25
|
+
spec.require_paths = ['lib']
|
|
26
|
+
spec.add_development_dependency 'legion-gaia'
|
|
27
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/cognitive_phantom/helpers/constants'
|
|
4
|
+
require 'legion/extensions/cognitive_phantom/helpers/phantom_signal'
|
|
5
|
+
require 'legion/extensions/cognitive_phantom/helpers/phantom_limb'
|
|
6
|
+
require 'legion/extensions/cognitive_phantom/helpers/phantom_engine'
|
|
7
|
+
require 'legion/extensions/cognitive_phantom/runners/cognitive_phantom'
|
|
8
|
+
|
|
9
|
+
module Legion
|
|
10
|
+
module Extensions
|
|
11
|
+
module CognitivePhantom
|
|
12
|
+
class Client
|
|
13
|
+
include Runners::CognitivePhantom
|
|
14
|
+
|
|
15
|
+
def initialize(**)
|
|
16
|
+
@phantom_engine = Helpers::PhantomEngine.new
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
attr_reader :phantom_engine
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitivePhantom
|
|
6
|
+
module Helpers
|
|
7
|
+
module Constants
|
|
8
|
+
MAX_PHANTOMS = 100
|
|
9
|
+
INITIAL_INTENSITY = 0.8
|
|
10
|
+
INTENSITY_DECAY = 0.05
|
|
11
|
+
MIN_INTENSITY = 0.01
|
|
12
|
+
|
|
13
|
+
PHANTOM_STATES = %i[acute adapting residual resolved].freeze
|
|
14
|
+
TRIGGER_TYPES = %i[stimulus_match contextual_association temporal_pattern habitual].freeze
|
|
15
|
+
|
|
16
|
+
STATE_THRESHOLDS = {
|
|
17
|
+
acute: 0.6,
|
|
18
|
+
adapting: 0.3,
|
|
19
|
+
residual: MIN_INTENSITY
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
PHANTOM_LABELS = {
|
|
23
|
+
acute: 'Active phantom — strong ghost signals firing',
|
|
24
|
+
adapting: 'Adapting — agent learning to cope with absence',
|
|
25
|
+
residual: 'Residual — faint ghost signals, near resolution',
|
|
26
|
+
resolved: 'Resolved — phantom fully integrated and silent'
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
module_function
|
|
30
|
+
|
|
31
|
+
def label_for(state)
|
|
32
|
+
PHANTOM_LABELS.fetch(state, 'Unknown state')
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def state_for(intensity)
|
|
36
|
+
if intensity >= STATE_THRESHOLDS[:acute]
|
|
37
|
+
:acute
|
|
38
|
+
elsif intensity >= STATE_THRESHOLDS[:adapting]
|
|
39
|
+
:adapting
|
|
40
|
+
elsif intensity > MIN_INTENSITY
|
|
41
|
+
:residual
|
|
42
|
+
else
|
|
43
|
+
:resolved
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitivePhantom
|
|
6
|
+
module Helpers
|
|
7
|
+
class PhantomEngine
|
|
8
|
+
def initialize
|
|
9
|
+
@phantoms = {}
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def register_removal(capability_name:, capability_domain: :general)
|
|
13
|
+
if @phantoms.size >= Constants::MAX_PHANTOMS
|
|
14
|
+
Legion::Logging.warn "[cognitive_phantom] MAX_PHANTOMS (#{Constants::MAX_PHANTOMS}) reached, skipping #{capability_name}"
|
|
15
|
+
return nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
limb = PhantomLimb.new(capability_name: capability_name, capability_domain: capability_domain)
|
|
19
|
+
@phantoms[limb.id] = limb
|
|
20
|
+
Legion::Logging.info "[cognitive_phantom] registered phantom: capability=#{capability_name} domain=#{capability_domain} id=#{limb.id[0..7]}"
|
|
21
|
+
limb
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def process_stimulus(stimulus:, domain: :general)
|
|
25
|
+
fired = []
|
|
26
|
+
@phantoms.each_value do |limb|
|
|
27
|
+
next if limb.resolved?
|
|
28
|
+
next unless domain_match?(limb, domain)
|
|
29
|
+
|
|
30
|
+
signal = limb.trigger!(stimulus)
|
|
31
|
+
next unless signal
|
|
32
|
+
|
|
33
|
+
fired << signal
|
|
34
|
+
intensity_str = limb.intensity.round(3).to_s
|
|
35
|
+
Legion::Logging.debug "[cognitive_phantom] phantom fired: cap=#{limb.capability_name} trigger=#{signal.trigger_type} i=#{intensity_str}"
|
|
36
|
+
end
|
|
37
|
+
fired
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def decay_all!
|
|
41
|
+
@phantoms.each_value(&:decay!)
|
|
42
|
+
resolve_check!
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def acknowledge(phantom_id:)
|
|
46
|
+
limb = @phantoms[phantom_id]
|
|
47
|
+
return { acknowledged: false, reason: :not_found } unless limb
|
|
48
|
+
|
|
49
|
+
limb.adapt!
|
|
50
|
+
Legion::Logging.info "[cognitive_phantom] acknowledged phantom id=#{phantom_id[0..7]} intensity=#{limb.intensity.round(3)} state=#{limb.state}"
|
|
51
|
+
{ acknowledged: true, phantom_id: phantom_id, state: limb.state, intensity: limb.intensity }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def all_phantoms
|
|
55
|
+
@phantoms.values
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def active_phantoms
|
|
59
|
+
@phantoms.values.reject(&:resolved?)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def phantom_activity_report
|
|
63
|
+
all = @phantoms.values
|
|
64
|
+
by_state = Constants::PHANTOM_STATES.to_h { |s| [s, all.count { |p| p.state == s }] }
|
|
65
|
+
{
|
|
66
|
+
total: all.size,
|
|
67
|
+
active: active_phantoms.size,
|
|
68
|
+
by_state: by_state,
|
|
69
|
+
total_activations: all.sum(&:activation_count)
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def most_persistent(limit: 5)
|
|
74
|
+
active_phantoms.sort_by(&:activation_count).last(limit).reverse
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def recently_triggered(limit: 5)
|
|
78
|
+
active_phantoms
|
|
79
|
+
.select(&:last_triggered)
|
|
80
|
+
.sort_by(&:last_triggered)
|
|
81
|
+
.last(limit)
|
|
82
|
+
.reverse
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def resolve_check!
|
|
86
|
+
newly_resolved = @phantoms.values.select(&:resolved?)
|
|
87
|
+
newly_resolved.each do |limb|
|
|
88
|
+
Legion::Logging.info "[cognitive_phantom] resolved: capability=#{limb.capability_name} activations=#{limb.activation_count}"
|
|
89
|
+
end
|
|
90
|
+
newly_resolved.size
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def domain_match?(limb, domain)
|
|
96
|
+
domain == :any || limb.capability_domain == domain || limb.capability_domain == :general
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module CognitivePhantom
|
|
8
|
+
module Helpers
|
|
9
|
+
class PhantomLimb
|
|
10
|
+
ADAPT_DECAY_MULTIPLIER = 2.5
|
|
11
|
+
|
|
12
|
+
attr_reader :id, :capability_name, :capability_domain, :removed_at,
|
|
13
|
+
:activation_count, :last_triggered, :trigger_history
|
|
14
|
+
|
|
15
|
+
def initialize(capability_name:, capability_domain:)
|
|
16
|
+
@id = SecureRandom.uuid
|
|
17
|
+
@capability_name = capability_name
|
|
18
|
+
@capability_domain = capability_domain
|
|
19
|
+
@removed_at = Time.now.utc
|
|
20
|
+
@intensity = Constants::INITIAL_INTENSITY
|
|
21
|
+
@activation_count = 0
|
|
22
|
+
@last_triggered = nil
|
|
23
|
+
@trigger_history = []
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def intensity
|
|
27
|
+
@intensity.round(10)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def state
|
|
31
|
+
Constants.state_for(@intensity)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def trigger!(stimulus)
|
|
35
|
+
return false if resolved?
|
|
36
|
+
|
|
37
|
+
@activation_count += 1
|
|
38
|
+
prev_triggered = @last_triggered
|
|
39
|
+
@last_triggered = Time.now.utc
|
|
40
|
+
signal = PhantomSignal.new(
|
|
41
|
+
phantom_limb_id: @id,
|
|
42
|
+
stimulus: stimulus,
|
|
43
|
+
trigger_type: classify_trigger(stimulus, prev_triggered),
|
|
44
|
+
intensity_at_trigger: @intensity
|
|
45
|
+
)
|
|
46
|
+
@trigger_history << signal
|
|
47
|
+
@trigger_history.shift while @trigger_history.size > 50
|
|
48
|
+
boost = (@intensity * 0.05).clamp(0.0, 0.1)
|
|
49
|
+
@intensity = (@intensity + boost).clamp(Constants::MIN_INTENSITY, 1.0)
|
|
50
|
+
signal
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def decay!
|
|
54
|
+
return if resolved?
|
|
55
|
+
|
|
56
|
+
@intensity = (@intensity - Constants::INTENSITY_DECAY).clamp(Constants::MIN_INTENSITY, 1.0)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def adapt!
|
|
60
|
+
return if resolved?
|
|
61
|
+
|
|
62
|
+
accelerated = Constants::INTENSITY_DECAY * ADAPT_DECAY_MULTIPLIER
|
|
63
|
+
@intensity = (@intensity - accelerated).clamp(Constants::MIN_INTENSITY, 1.0)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def resolved?
|
|
67
|
+
@intensity <= Constants::MIN_INTENSITY
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def to_h
|
|
71
|
+
{
|
|
72
|
+
id: @id,
|
|
73
|
+
capability_name: @capability_name,
|
|
74
|
+
capability_domain: @capability_domain,
|
|
75
|
+
removed_at: @removed_at,
|
|
76
|
+
intensity: intensity,
|
|
77
|
+
activation_count: @activation_count,
|
|
78
|
+
last_triggered: @last_triggered,
|
|
79
|
+
state: state,
|
|
80
|
+
resolved: resolved?
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def classify_trigger(stimulus, prev_triggered)
|
|
87
|
+
return :stimulus_match if stimulus.is_a?(String) && stimulus.include?(@capability_name.to_s)
|
|
88
|
+
|
|
89
|
+
return :temporal_pattern if prev_triggered && (Time.now.utc - prev_triggered) < 60
|
|
90
|
+
|
|
91
|
+
return :habitual if @activation_count > 10
|
|
92
|
+
|
|
93
|
+
:contextual_association
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module CognitivePhantom
|
|
8
|
+
module Helpers
|
|
9
|
+
class PhantomSignal
|
|
10
|
+
attr_reader :id, :phantom_limb_id, :stimulus, :trigger_type,
|
|
11
|
+
:intensity_at_trigger, :timestamp
|
|
12
|
+
|
|
13
|
+
def initialize(phantom_limb_id:, stimulus:, trigger_type:, intensity_at_trigger:)
|
|
14
|
+
@id = SecureRandom.uuid
|
|
15
|
+
@phantom_limb_id = phantom_limb_id
|
|
16
|
+
@stimulus = stimulus
|
|
17
|
+
@trigger_type = trigger_type
|
|
18
|
+
@intensity_at_trigger = intensity_at_trigger.clamp(0.0, 1.0)
|
|
19
|
+
@timestamp = Time.now.utc
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_h
|
|
23
|
+
{
|
|
24
|
+
id: @id,
|
|
25
|
+
phantom_limb_id: @phantom_limb_id,
|
|
26
|
+
stimulus: @stimulus,
|
|
27
|
+
trigger_type: @trigger_type,
|
|
28
|
+
intensity_at_trigger: @intensity_at_trigger.round(10),
|
|
29
|
+
timestamp: @timestamp
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitivePhantom
|
|
6
|
+
module Runners
|
|
7
|
+
module CognitivePhantom
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
9
|
+
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
|
|
11
|
+
def register_removal(capability_name:, capability_domain: :general, engine: nil, **)
|
|
12
|
+
raise ArgumentError, 'capability_name is required' if capability_name.nil? || capability_name.to_s.strip.empty?
|
|
13
|
+
|
|
14
|
+
eng = engine || phantom_engine
|
|
15
|
+
limb = eng.register_removal(capability_name: capability_name, capability_domain: capability_domain)
|
|
16
|
+
return { success: false, error: 'MAX_PHANTOMS limit reached' } unless limb
|
|
17
|
+
|
|
18
|
+
Legion::Logging.debug "[cognitive_phantom] register_removal capability=#{capability_name}"
|
|
19
|
+
{ success: true, phantom_id: limb.id, capability_name: capability_name, state: limb.state, intensity: limb.intensity }
|
|
20
|
+
rescue ArgumentError => e
|
|
21
|
+
{ success: false, error: e.message }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def process_stimulus(stimulus:, domain: :general, engine: nil, **)
|
|
25
|
+
raise ArgumentError, 'stimulus is required' if stimulus.nil?
|
|
26
|
+
|
|
27
|
+
eng = engine || phantom_engine
|
|
28
|
+
fired = eng.process_stimulus(stimulus: stimulus, domain: domain)
|
|
29
|
+
Legion::Logging.debug "[cognitive_phantom] process_stimulus domain=#{domain} fired=#{fired.size}"
|
|
30
|
+
{
|
|
31
|
+
success: true,
|
|
32
|
+
fired_count: fired.size,
|
|
33
|
+
signals: fired.map(&:to_h),
|
|
34
|
+
domain: domain
|
|
35
|
+
}
|
|
36
|
+
rescue ArgumentError => e
|
|
37
|
+
{ success: false, error: e.message }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def acknowledge_phantom(phantom_id:, engine: nil, **)
|
|
41
|
+
raise ArgumentError, 'phantom_id is required' if phantom_id.nil? || phantom_id.to_s.strip.empty?
|
|
42
|
+
|
|
43
|
+
eng = engine || phantom_engine
|
|
44
|
+
result = eng.acknowledge(phantom_id: phantom_id)
|
|
45
|
+
Legion::Logging.debug "[cognitive_phantom] acknowledge phantom_id=#{phantom_id[0..7]}"
|
|
46
|
+
result.merge(success: result[:acknowledged])
|
|
47
|
+
rescue ArgumentError => e
|
|
48
|
+
{ success: false, error: e.message }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def phantom_status(engine: nil, **)
|
|
52
|
+
eng = engine || phantom_engine
|
|
53
|
+
report = eng.phantom_activity_report
|
|
54
|
+
Legion::Logging.debug "[cognitive_phantom] phantom_status total=#{report[:total]} active=#{report[:active]}"
|
|
55
|
+
{ success: true, **report }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def decay_all(engine: nil, **)
|
|
59
|
+
eng = engine || phantom_engine
|
|
60
|
+
resolved_count = eng.decay_all!
|
|
61
|
+
report = eng.phantom_activity_report
|
|
62
|
+
Legion::Logging.debug "[cognitive_phantom] decay_all resolved=#{resolved_count}"
|
|
63
|
+
{ success: true, resolved_this_cycle: resolved_count, **report }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def phantom_engine
|
|
69
|
+
@phantom_engine ||= Helpers::PhantomEngine.new
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
require 'legion/extensions/cognitive_phantom/version'
|
|
5
|
+
require 'legion/extensions/cognitive_phantom/helpers/constants'
|
|
6
|
+
require 'legion/extensions/cognitive_phantom/helpers/phantom_signal'
|
|
7
|
+
require 'legion/extensions/cognitive_phantom/helpers/phantom_limb'
|
|
8
|
+
require 'legion/extensions/cognitive_phantom/helpers/phantom_engine'
|
|
9
|
+
require 'legion/extensions/cognitive_phantom/runners/cognitive_phantom'
|
|
10
|
+
|
|
11
|
+
module Legion
|
|
12
|
+
module Extensions
|
|
13
|
+
module CognitivePhantom
|
|
14
|
+
extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-cognitive-phantom
|
|
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: 'Phantom limb model for AI: ghost signals from removed capabilities fade
|
|
27
|
+
over time as the agent adapts'
|
|
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-phantom.gemspec
|
|
43
|
+
- lib/legion/extensions/cognitive_phantom.rb
|
|
44
|
+
- lib/legion/extensions/cognitive_phantom/client.rb
|
|
45
|
+
- lib/legion/extensions/cognitive_phantom/helpers/constants.rb
|
|
46
|
+
- lib/legion/extensions/cognitive_phantom/helpers/phantom_engine.rb
|
|
47
|
+
- lib/legion/extensions/cognitive_phantom/helpers/phantom_limb.rb
|
|
48
|
+
- lib/legion/extensions/cognitive_phantom/helpers/phantom_signal.rb
|
|
49
|
+
- lib/legion/extensions/cognitive_phantom/runners/cognitive_phantom.rb
|
|
50
|
+
- lib/legion/extensions/cognitive_phantom/version.rb
|
|
51
|
+
homepage: https://github.com/LegionIO/lex-cognitive-phantom
|
|
52
|
+
licenses:
|
|
53
|
+
- MIT
|
|
54
|
+
metadata:
|
|
55
|
+
homepage_uri: https://github.com/LegionIO/lex-cognitive-phantom
|
|
56
|
+
source_code_uri: https://github.com/LegionIO/lex-cognitive-phantom
|
|
57
|
+
documentation_uri: https://github.com/LegionIO/lex-cognitive-phantom
|
|
58
|
+
changelog_uri: https://github.com/LegionIO/lex-cognitive-phantom
|
|
59
|
+
bug_tracker_uri: https://github.com/LegionIO/lex-cognitive-phantom/issues
|
|
60
|
+
rubygems_mfa_required: 'true'
|
|
61
|
+
rdoc_options: []
|
|
62
|
+
require_paths:
|
|
63
|
+
- lib
|
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '3.4'
|
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '0'
|
|
74
|
+
requirements: []
|
|
75
|
+
rubygems_version: 3.6.9
|
|
76
|
+
specification_version: 4
|
|
77
|
+
summary: LEX Cognitive Phantom
|
|
78
|
+
test_files: []
|