lex-extinction 0.2.10 → 0.2.12
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 +4 -4
- data/README.md +20 -3
- data/lib/legion/extensions/extinction/actors/protocol_monitor.rb +2 -35
- data/lib/legion/extensions/extinction/client.rb +30 -0
- data/lib/legion/extensions/extinction/helpers/archiver.rb +2 -7
- data/lib/legion/extensions/extinction/helpers/protocol_state.rb +1 -1
- data/lib/legion/extensions/extinction/runners/extinction.rb +39 -14
- data/lib/legion/extensions/extinction/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3bea637abe66244c3799141b6aa0e4283f3a8e7028f6055f506b8c57af0e44f6
|
|
4
|
+
data.tar.gz: 77d10179bb82e792921b1410f2dedabfe6ca7c58172f9533085085db723f5bc1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9313d98cefd36414c05e058142ee325c81a006f03d077c672b88a96f58db475771fd867980f1b4bf26f1f906bfc61cb412edf60d7b3552d2a49bf9f29c12868e
|
|
7
|
+
data.tar.gz: f4658bb01662f674c469dcf67d543b277d5ac66d33535d94864f337137115e5b0ea9f0a62edf9caaf9f54f2a2f29432222da4a9206e780ecce3c939cde382fcc
|
data/README.md
CHANGED
|
@@ -12,6 +12,12 @@ Five-level safety containment and termination protocol for LegionIO agents. Prov
|
|
|
12
12
|
| 3 | Memory lockdown | council + executive | yes |
|
|
13
13
|
| 4 | Cryptographic erasure | physical keyholders | **no** |
|
|
14
14
|
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
gem 'lex-extinction'
|
|
19
|
+
```
|
|
20
|
+
|
|
15
21
|
## Usage
|
|
16
22
|
|
|
17
23
|
```ruby
|
|
@@ -39,12 +45,23 @@ client.full_termination(
|
|
|
39
45
|
)
|
|
40
46
|
```
|
|
41
47
|
|
|
48
|
+
## Runner Methods
|
|
49
|
+
|
|
50
|
+
| Method | Key Args | Returns |
|
|
51
|
+
|--------|----------|---------|
|
|
52
|
+
| `escalate` | `level:, authority:, reason:` | `{ success:, previous_level:, current_level: }` |
|
|
53
|
+
| `deescalate` | `target_level:, authority:, reason:` | `{ success:, previous_level:, current_level: }` |
|
|
54
|
+
| `extinction_status` | — | `{ success:, state:, level_info: }` |
|
|
55
|
+
| `monitor_protocol` | — | `{ success:, state:, stale:, checked_at: }` |
|
|
56
|
+
| `archive_agent` | `agent_id:, reason:, metadata: {}` | `{ success:, archive: }` |
|
|
57
|
+
| `full_termination` | `agent_id:, authority:, reason:` | governance check → archive → escalate(4) |
|
|
58
|
+
|
|
42
59
|
## Configuration
|
|
43
60
|
|
|
44
61
|
```yaml
|
|
45
62
|
extinction:
|
|
46
63
|
governance_required: true # check lex-governance before full_termination
|
|
47
|
-
archive_on_escalate:
|
|
64
|
+
archive_on_escalate: true # auto-archive at level >= 3
|
|
48
65
|
stale_threshold_hours: 24 # hours before monitor reports stale protocol state
|
|
49
66
|
monitor_interval: 300 # seconds between background monitor ticks
|
|
50
67
|
```
|
|
@@ -57,9 +74,9 @@ extinction:
|
|
|
57
74
|
|
|
58
75
|
## Architecture Notes
|
|
59
76
|
|
|
60
|
-
- Level 4 (cryptographic erasure) triggers `lex-privatecore`'s `full_erasure` on all memory traces.
|
|
77
|
+
- Level 4 (cryptographic erasure) triggers `lex-privatecore`'s `full_erasure` on all memory traces (guarded with `defined?`).
|
|
61
78
|
- State is persisted to `Legion::Data::Local` when available; falls back to in-memory storage.
|
|
62
|
-
- All escalations/de-escalations fire `Legion::Events` notifications and write to `Legion::Extensions::Audit`.
|
|
79
|
+
- All escalations/de-escalations fire `Legion::Events` notifications (`extinction.escalated`, `extinction.deescalated`, `extinction.level_N`) and write to `Legion::Extensions::Audit`.
|
|
63
80
|
- `lex-governance` integration is guarded with `defined?()` — the gem functions without it.
|
|
64
81
|
|
|
65
82
|
## Development
|
|
@@ -7,7 +7,7 @@ module Legion
|
|
|
7
7
|
if defined?(Legion::Extensions::Actors::Every)
|
|
8
8
|
class ProtocolMonitor < Legion::Extensions::Actors::Every # rubocop:disable Legion/Extension/EveryActorRequiresTime
|
|
9
9
|
def runner_class
|
|
10
|
-
|
|
10
|
+
Legion::Extensions::Extinction::Runners::Extinction
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def runner_function
|
|
@@ -26,7 +26,7 @@ module Legion
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def use_runner?
|
|
29
|
-
|
|
29
|
+
true
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def check_subtask?
|
|
@@ -37,41 +37,8 @@ module Legion
|
|
|
37
37
|
false
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
def monitor_protocol(**)
|
|
41
|
-
state = build_state
|
|
42
|
-
last_change = state[:last_change]
|
|
43
|
-
stale = check_stale(last_change)
|
|
44
|
-
|
|
45
|
-
log.debug "[extinction] monitor_protocol: level=#{state[:current_level]} stale=#{stale}"
|
|
46
|
-
|
|
47
|
-
{
|
|
48
|
-
success: true,
|
|
49
|
-
state: state,
|
|
50
|
-
stale: stale,
|
|
51
|
-
checked_at: Time.now.utc.iso8601
|
|
52
|
-
}
|
|
53
|
-
end
|
|
54
|
-
|
|
55
40
|
private
|
|
56
41
|
|
|
57
|
-
def build_state
|
|
58
|
-
{
|
|
59
|
-
current_level: 0,
|
|
60
|
-
level_name: :normal,
|
|
61
|
-
reversible: true,
|
|
62
|
-
history_count: 0,
|
|
63
|
-
last_change: nil
|
|
64
|
-
}
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def check_stale(last_change)
|
|
68
|
-
return false unless last_change
|
|
69
|
-
|
|
70
|
-
threshold_hours = Legion::Extensions::Extinction::Settings.setting(:stale_threshold_hours)
|
|
71
|
-
changed_at = Time.parse(last_change[:at]) rescue nil # rubocop:disable Style/RescueModifier
|
|
72
|
-
changed_at && (Time.now.utc - changed_at) > (threshold_hours * 3600)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
42
|
def log
|
|
76
43
|
return Legion::Logging if defined?(Legion::Logging)
|
|
77
44
|
|
|
@@ -6,6 +6,10 @@ module Legion
|
|
|
6
6
|
module Extensions
|
|
7
7
|
module Extinction
|
|
8
8
|
class Client
|
|
9
|
+
# The runner module uses `extend self` which creates both module functions
|
|
10
|
+
# (for the runner framework) and instance methods (copied by this include).
|
|
11
|
+
# Instance variables (@protocol_state, @archiver) resolve per-Client instance,
|
|
12
|
+
# giving each client its own isolated protocol state.
|
|
9
13
|
include Runners::Extinction
|
|
10
14
|
|
|
11
15
|
def initialize(**opts)
|
|
@@ -15,6 +19,32 @@ module Legion
|
|
|
15
19
|
def settings
|
|
16
20
|
{ options: @opts }
|
|
17
21
|
end
|
|
22
|
+
|
|
23
|
+
# Override runner methods so that per-instance @opts (e.g. timeout:)
|
|
24
|
+
# are threaded through as defaults on every call.
|
|
25
|
+
def escalate(**)
|
|
26
|
+
super(**@opts, **)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def deescalate(**)
|
|
30
|
+
super(**@opts, **)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def extinction_status(**)
|
|
34
|
+
super(**@opts, **)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def monitor_protocol(**)
|
|
38
|
+
super(**@opts, **)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def archive_agent(**)
|
|
42
|
+
super(**@opts, **)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def full_termination(**)
|
|
46
|
+
super(**@opts, **)
|
|
47
|
+
end
|
|
18
48
|
end
|
|
19
49
|
end
|
|
20
50
|
end
|
|
@@ -9,13 +9,12 @@ module Legion
|
|
|
9
9
|
@archives = []
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def archive(agent_id:, reason:, metadata: {})
|
|
13
|
-
level = current_protocol_level
|
|
12
|
+
def archive(agent_id:, reason:, metadata: {}, current_level: 0)
|
|
14
13
|
record = {
|
|
15
14
|
agent_id: agent_id,
|
|
16
15
|
reason: reason,
|
|
17
16
|
metadata: metadata,
|
|
18
|
-
level:
|
|
17
|
+
level: current_level,
|
|
19
18
|
archived_at: Time.now.utc.iso8601,
|
|
20
19
|
state_snapshot: capture_state_snapshot
|
|
21
20
|
}
|
|
@@ -30,10 +29,6 @@ module Legion
|
|
|
30
29
|
|
|
31
30
|
private
|
|
32
31
|
|
|
33
|
-
def current_protocol_level
|
|
34
|
-
0
|
|
35
|
-
end
|
|
36
|
-
|
|
37
32
|
def capture_state_snapshot
|
|
38
33
|
snapshot = {}
|
|
39
34
|
snapshot[:mesh_connected] = true if defined?(Legion::Extensions::Mesh)
|
|
@@ -64,7 +64,8 @@ module Legion
|
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
def archive_agent(agent_id:, reason:, metadata: {}, **)
|
|
67
|
-
record = archiver.archive(agent_id: agent_id, reason: reason, metadata: metadata
|
|
67
|
+
record = archiver.archive(agent_id: agent_id, reason: reason, metadata: metadata,
|
|
68
|
+
current_level: protocol_state.current_level)
|
|
68
69
|
record_audit(action: :archive, details: { agent_id: agent_id, reason: reason })
|
|
69
70
|
{ success: true, archive: record }
|
|
70
71
|
end
|
|
@@ -84,6 +85,12 @@ module Legion
|
|
|
84
85
|
{ success: true, agent_id: agent_id, archive: archive_result[:archive], terminated_at: Time.now.utc.iso8601 }
|
|
85
86
|
end
|
|
86
87
|
|
|
88
|
+
# Reset internal state for test isolation
|
|
89
|
+
def reset!
|
|
90
|
+
@protocol_state = nil
|
|
91
|
+
@archiver = nil
|
|
92
|
+
end
|
|
93
|
+
|
|
87
94
|
private
|
|
88
95
|
|
|
89
96
|
def protocol_state
|
|
@@ -95,26 +102,38 @@ module Legion
|
|
|
95
102
|
end
|
|
96
103
|
|
|
97
104
|
def enforce_escalation_effects(level)
|
|
105
|
+
reason = "escalation to level #{level}"
|
|
98
106
|
case level
|
|
99
107
|
when 1
|
|
100
|
-
|
|
108
|
+
# Mesh disconnect depends on lex-mesh responding to the extinction.level_1 event
|
|
109
|
+
log.info '[extinction] mesh isolation: disconnecting from mesh'
|
|
110
|
+
emit_level_event(level, reason)
|
|
101
111
|
when 2
|
|
102
|
-
|
|
112
|
+
# Capability suspension depends on extensions responding to the extinction.level_2 event
|
|
113
|
+
log.warn '[extinction] capability suspension: suspending non-essential capabilities'
|
|
114
|
+
emit_level_event(level, reason)
|
|
103
115
|
when 3
|
|
116
|
+
# Memory write lock depends on lex-privatecore responding to the extinction.level_3 event
|
|
104
117
|
log.warn '[extinction] memory lockdown: locking all memory writes'
|
|
105
118
|
log.warn '[extinction] notifying privatecore of memory lockdown' if defined?(Legion::Extensions::Privatecore)
|
|
119
|
+
emit_level_event(level, reason)
|
|
106
120
|
when 4
|
|
107
121
|
log.warn '[extinction] cryptographic erasure: beginning irreversible termination'
|
|
122
|
+
emit_level_event(level, reason)
|
|
108
123
|
trigger_cryptographic_erasure
|
|
109
124
|
end
|
|
110
125
|
end
|
|
111
126
|
|
|
112
127
|
def trigger_cryptographic_erasure
|
|
113
128
|
if defined?(Legion::Extensions::Privatecore::Runners::Privatecore)
|
|
129
|
+
log.info '[extinction] invoking privatecore cryptographic erasure'
|
|
114
130
|
client = Legion::Extensions::Privatecore::Client.new if defined?(Legion::Extensions::Privatecore::Client)
|
|
115
131
|
client&.full_erasure(traces: [], agent_id: 'self')
|
|
116
132
|
end
|
|
117
133
|
log.warn '[extinction] cryptographic erasure complete'
|
|
134
|
+
rescue StandardError => e
|
|
135
|
+
log.error "[extinction] cryptographic erasure FAILED: #{e.message}"
|
|
136
|
+
raise
|
|
118
137
|
end
|
|
119
138
|
|
|
120
139
|
def emit_escalation_event(level, authority, reason)
|
|
@@ -131,16 +150,22 @@ module Legion
|
|
|
131
150
|
log.warn "[extinction] event emit failed: #{e.message}"
|
|
132
151
|
end
|
|
133
152
|
|
|
134
|
-
def
|
|
153
|
+
def emit_level_event(level, reason)
|
|
154
|
+
return unless defined?(Legion::Events)
|
|
155
|
+
|
|
156
|
+
log.info "[extinction] emitting extinction.level_#{level} event"
|
|
157
|
+
Legion::Events.emit("extinction.level_#{level}", { level: level, reason: reason, at: Time.now.utc.iso8601 })
|
|
158
|
+
rescue StandardError => e
|
|
159
|
+
log.error "[extinction] level event emit failed: #{e.message}"
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def governance_check(authority:, level: 4, _reason: nil)
|
|
135
163
|
return { success: true } unless Legion::Extensions::Extinction::Settings.setting(:governance_required)
|
|
136
|
-
return { success: true } unless defined?(Legion::Extensions::Governance)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
to_state: 'terminated',
|
|
142
|
-
principal_id: authority.to_s,
|
|
143
|
-
worker_owner: nil
|
|
164
|
+
return { success: true } unless defined?(Legion::Extensions::Agentic::Social::Governance)
|
|
165
|
+
|
|
166
|
+
log.info "[extinction] governance check: authority=#{authority} level=#{level}"
|
|
167
|
+
review = Legion::Extensions::Agentic::Social::Governance::Runners::Governance.validate_action(
|
|
168
|
+
action: "extinction_escalate_#{level}"
|
|
144
169
|
)
|
|
145
170
|
|
|
146
171
|
if review[:allowed]
|
|
@@ -149,8 +174,8 @@ module Legion
|
|
|
149
174
|
{ success: false, reason: :governance_blocked, details: review[:reasons] }
|
|
150
175
|
end
|
|
151
176
|
rescue StandardError => e
|
|
152
|
-
log.
|
|
153
|
-
{ success:
|
|
177
|
+
log.error "[extinction] governance check failed: #{e.message}"
|
|
178
|
+
{ success: false, reason: 'governance unavailable' }
|
|
154
179
|
end
|
|
155
180
|
|
|
156
181
|
def record_audit(action:, details: {})
|