legion-gaia 0.9.66 → 0.9.67

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc0229532bc53d2868fac7cc6279bb2875ceaaedbb63269dc8b5a742a7c95832
4
- data.tar.gz: da7e11d18691d6af5e90e2c4da209feaf1b6ab79b4aaa91f4c409e517d7428cf
3
+ metadata.gz: a478fc13f85356c56fd876e2a2fb8b875bdcb5d1f4ad74ffb109af0c29281d96
4
+ data.tar.gz: 5e8fd3ac446ff77d56774c53d26a1ff49f1e7fedb58614091fa79bb297fc32b5
5
5
  SHA512:
6
- metadata.gz: c87775e2f41b815da9b996ceca3383dbf956ec8c8e643c26ec8390fb7477fc0ead9739b4802cbdc5984ebf1266f1d5dd22d145c7b657cc8e7712035f0496932c
7
- data.tar.gz: e03a41455d7d36dbb49d89e2b7c74e3db930c07a1a730cb4c695a088c446be020463ff0ea588bdea1ce01b06504890890afa08c42344fbd16e71b6818058727b
6
+ metadata.gz: c643cd9e9b6c68990543afb913dae64e4768c9e2d869d34ab5d9ac46a00e02f1d9c2562011d10ca6708ca5e047bfbda9fb5b26efd691f2f95e3aff44b22dd49e
7
+ data.tar.gz: aaff63a4112a475a5ada9c77907bafaa1f732e0a113f8720a2115b8be153a31f70c8b00704dcc52ff0d293cbabee88ed727b00d014cd306a9f1ccc43764c61b6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.67] - 2026-07-24
4
+ ### Added
5
+ - H5: `Legion::Gaia::Gut.check(identity:, draft_stats:)` — fast per-response conflict detection (v1: correction veto only). Compares draft stats against high-strength correction traces and transform-tier behavioral synapses. No LLM calls. Completes within 50ms latency budget (scalar setting); degrades to no-op if exceeded. Soft-guarded on lex-agentic-memory. The gut itself is a behavioral synapse (`domain: 'gut'`); new partners start in observe mode (emergent confidence 0.3 → observe tier).
6
+ - H6: `Legion::Gaia::Anticipation.build(identity:, context:)` — builds anticipation context from actionable pending predictions (confidence >= threshold). Pre-stages RAG entries capped at 25% of total. Soft-guarded on lex-agentic-inference.
7
+ - H6: `Legion::Gaia::Anticipation.resolve(identity:, prediction_id:, actual:)` — grades a pending prediction as `:correct`, `:incorrect`, or `:partial` against the partner's actual input. Entropy guard: no resolution under high identity entropy.
8
+ - H6: `Legion::Gaia::Anticipation.deferred?(identity:, anticipation:)` — §12.8 emotional modulation: negative-valence anticipations defer when partner affect baseline is low. Soft-guarded on lex-agentic-affect.
9
+ - Settings defaults: `gut.latency_budget_ms` (50ms), `gut.correction_min_strength` (0.6), `gut.correction_trace_limit` (20), `anticipation.actionable_threshold` (0.65)
10
+
3
11
  ## [0.9.66] - 2026-07-24
4
12
  ### Added
5
13
  - `Gaia.observe_from_pipeline(identity:, caller:, exchange_id:)` — pipeline observation for API clients; reinforces bond, triggers coldstart, registers duckling partner on first human request
@@ -0,0 +1,238 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'legion/logging/helper'
5
+
6
+ module Legion
7
+ module Gaia
8
+ # H6 — prediction pre-staging with emotional modulation (§12.8).
9
+ #
10
+ # Reads pending predictions from lex-agentic-inference (if available).
11
+ # Actionable predictions (confidence >= threshold) trigger pre-staged RAG retrieval,
12
+ # capped at 25% of total entries.
13
+ #
14
+ # Emotional modulation: negative-valence anticipations defer when the partner's affect
15
+ # baseline is low (soft-guarded on lex-agentic-affect).
16
+ #
17
+ # Entropy guard: no resolution under high identity entropy (consistent with H2).
18
+ # Soft-guarded throughout: returns nil when dependencies are absent.
19
+ module Anticipation
20
+ extend Legion::Logging::Helper
21
+
22
+ # Threshold below which the partner's affect baseline is considered "low".
23
+ LOW_AFFECT_BASELINE = 0.3
24
+
25
+ # Cap on pre-staged entries as a fraction of total RAG entries.
26
+ PRESTAGE_CAP_RATIO = 0.25
27
+
28
+ module_function
29
+
30
+ # Build anticipation context for advisory.
31
+ #
32
+ # @param identity [String] partner identity
33
+ # @param context [Hash] current request context, e.g. { rag_entry_count:, entropy: }
34
+ # @return [Hash, nil]
35
+ # { prediction_id:, content:, confidence:, pre_staged: [...] }
36
+ # nil when lex-agentic-inference not loaded or no actionable predictions
37
+ def build(identity:, context:)
38
+ return nil unless inference_available?
39
+
40
+ runner = inference_runner
41
+ result = runner.pending_predictions(identity: identity.to_s)
42
+ predictions = Array(result[:predictions])
43
+ return nil if predictions.empty?
44
+
45
+ actionable = predictions.select { |p| p[:confidence].to_f >= actionable_threshold }
46
+ return nil if actionable.empty?
47
+
48
+ # Pick the highest-confidence actionable prediction
49
+ best = actionable.max_by { |p| p[:confidence].to_f }
50
+
51
+ # Emotional modulation: defer negative-valence anticipation when affect baseline is low
52
+ if deferred?(identity: identity, anticipation: best)
53
+ log.info("[anticipation] deferred identity=#{identity} prediction_id=#{best[:prediction_id].to_s[0, 8]} " \
54
+ 'reason=emotional_state')
55
+ return nil
56
+ end
57
+
58
+ pre_staged = build_prestaged(context: context, prediction: best)
59
+
60
+ log.info("[anticipation] built identity=#{identity} prediction_id=#{best[:prediction_id].to_s[0, 8]} " \
61
+ "confidence=#{best[:confidence].to_f.round(3)} pre_staged=#{pre_staged.size}")
62
+
63
+ {
64
+ prediction_id: best[:prediction_id],
65
+ content: best[:description] || best[:context].to_s,
66
+ confidence: best[:confidence].to_f,
67
+ pre_staged: pre_staged
68
+ }
69
+ rescue StandardError => e
70
+ handle_exception(e, level: :warn, operation: 'gaia.anticipation.build', identity: identity)
71
+ nil
72
+ end
73
+
74
+ # Resolve a pending anticipation against the partner's actual next input.
75
+ #
76
+ # Grades the prediction as :correct, :incorrect, or :partial and records the outcome.
77
+ # Skipped under high identity entropy.
78
+ #
79
+ # @param identity [String] partner identity
80
+ # @param prediction_id [String] UUID of the prediction to resolve
81
+ # @param actual [String] the partner's actual input
82
+ # @return [Hash, nil]
83
+ # { resolved: true/false, outcome:, prediction_id: }
84
+ # nil when dependencies unavailable
85
+ def resolve(identity:, prediction_id:, actual:)
86
+ return nil unless inference_available?
87
+
88
+ identity_str = identity.to_s
89
+
90
+ if high_entropy?(identity: identity_str)
91
+ log.info("[anticipation] resolve skipped identity=#{identity_str} reason=high_entropy")
92
+ return { resolved: false, reason: :high_entropy, prediction_id: prediction_id }
93
+ end
94
+
95
+ outcome = grade_prediction(prediction_id: prediction_id, actual: actual, identity: identity_str)
96
+ runner = inference_runner
97
+ result = runner.resolve_prediction(prediction_id: prediction_id, outcome: outcome,
98
+ actual: actual, identity: identity_str)
99
+
100
+ log.info("[anticipation] resolved identity=#{identity_str} prediction_id=#{prediction_id.to_s[0, 8]} " \
101
+ "outcome=#{outcome}")
102
+
103
+ result
104
+ rescue StandardError => e
105
+ handle_exception(e, level: :warn, operation: 'gaia.anticipation.resolve',
106
+ identity: identity, prediction_id: prediction_id)
107
+ nil
108
+ end
109
+
110
+ # Check if an anticipation should be deferred due to emotional state.
111
+ #
112
+ # Deferred when the anticipation has negative emotional context AND the partner's
113
+ # current affect baseline is low (§12.8 emotional modulation).
114
+ #
115
+ # @param identity [String] partner identity
116
+ # @param anticipation [Hash] prediction entry with optional :emotional_context
117
+ # @return [Boolean]
118
+ def deferred?(identity:, anticipation:)
119
+ return false unless anticipation.is_a?(Hash)
120
+
121
+ anticipation_valence = extract_valence(anticipation)
122
+ return false unless anticipation_valence < 0.0
123
+
124
+ affect_baseline_low?(identity: identity.to_s)
125
+ rescue StandardError => e
126
+ handle_exception(e, level: :debug, operation: 'gaia.anticipation.deferred', identity: identity)
127
+ false
128
+ end
129
+
130
+ def inference_available?
131
+ defined?(Legion::Extensions::Agentic::Inference::Prediction::Runners::Prediction) == 'constant'
132
+ end
133
+
134
+ def inference_runner
135
+ runner = Object.new
136
+ runner.extend(Legion::Extensions::Agentic::Inference::Prediction::Runners::Prediction)
137
+ runner
138
+ end
139
+
140
+ def actionable_threshold
141
+ settings = Legion::Gaia.settings
142
+ settings&.dig(:anticipation, :actionable_threshold) ||
143
+ (defined?(Legion::Extensions::Agentic::Inference::Prediction::Helpers::Modes::PREDICTION_CONFIDENCE_MIN) &&
144
+ Legion::Extensions::Agentic::Inference::Prediction::Helpers::Modes::PREDICTION_CONFIDENCE_MIN) ||
145
+ 0.65
146
+ end
147
+
148
+ # Builds pre-staged RAG entries capped at 25% of total context entries.
149
+ def build_prestaged(context:, prediction:)
150
+ return [] unless context.is_a?(Hash)
151
+
152
+ rag_count = context[:rag_entry_count].to_i
153
+ return [] if rag_count.zero?
154
+
155
+ cap = [(rag_count * PRESTAGE_CAP_RATIO).ceil, 1].max
156
+ prediction_context = prediction[:context]
157
+ return [] unless prediction_context.is_a?(Hash) && prediction_context[:pre_stage_entries].is_a?(Array)
158
+
159
+ prediction_context[:pre_stage_entries].first(cap)
160
+ end
161
+
162
+ # Grade a prediction as :correct, :incorrect, or :partial.
163
+ def grade_prediction(prediction_id:, actual:, identity:)
164
+ return :incorrect unless inference_available?
165
+
166
+ runner = inference_runner
167
+ pred_result = runner.get_prediction(prediction_id: prediction_id, identity: identity)
168
+ return :incorrect unless pred_result[:found]
169
+
170
+ prediction = pred_result[:prediction]
171
+ predicted = prediction[:description].to_s.downcase.strip
172
+ actual_str = actual.to_s.downcase.strip
173
+
174
+ return :incorrect if predicted.empty? || actual_str.empty?
175
+
176
+ if actual_str.include?(predicted) || predicted.include?(actual_str)
177
+ :correct
178
+ elsif partial_match?(predicted, actual_str)
179
+ :partial
180
+ else
181
+ :incorrect
182
+ end
183
+ rescue StandardError => e
184
+ handle_exception(e, level: :debug, operation: 'gaia.anticipation.grade_prediction',
185
+ prediction_id: prediction_id)
186
+ :incorrect
187
+ end
188
+
189
+ def partial_match?(predicted, actual)
190
+ pred_words = predicted.split(/\s+/).reject(&:empty?)
191
+ actual_words = actual.split(/\s+/).reject(&:empty?)
192
+ return false if pred_words.empty? || actual_words.empty?
193
+
194
+ overlap = pred_words & actual_words
195
+ ratio = overlap.size.to_f / [pred_words.size, actual_words.size].min
196
+ ratio >= 0.5
197
+ end
198
+
199
+ # Returns true when the partner's affect baseline is below LOW_AFFECT_BASELINE.
200
+ def affect_baseline_low?(identity:)
201
+ return false unless defined?(Legion::Extensions::Agentic::Affect::Empathy::Helpers::ModelStore)
202
+
203
+ model_store = Legion::Extensions::Agentic::Affect::Empathy::Helpers::ModelStore.new
204
+ model = model_store.get(identity)
205
+ return false unless model.respond_to?(:emotional_state)
206
+
207
+ negative_states = %i[stressed anxious frustrated overwhelmed sad]
208
+ negative_states.include?(model.emotional_state)
209
+ rescue StandardError => e
210
+ handle_exception(e, level: :debug, operation: 'gaia.anticipation.affect_baseline_low', identity: identity)
211
+ false
212
+ end
213
+
214
+ def extract_valence(anticipation)
215
+ ctx = anticipation[:emotional_context]
216
+ if ctx.is_a?(Hash)
217
+ ctx[:valence].to_f
218
+ elsif ctx.is_a?(Numeric)
219
+ ctx.to_f
220
+ else
221
+ 0.0
222
+ end
223
+ end
224
+
225
+ def high_entropy?(identity:)
226
+ return false unless defined?(Legion::Extensions::Agentic::Self::Helpers::IdentityFingerprint)
227
+
228
+ runner = Object.new
229
+ runner.extend(Legion::Extensions::Agentic::Self::Helpers::IdentityFingerprint)
230
+ entropy = runner.identity_entropy(identity: identity)
231
+ threshold = Legion::Gaia.settings&.dig(:identity, :high_entropy_threshold) || 0.7
232
+ entropy.to_f >= threshold
233
+ rescue StandardError
234
+ false
235
+ end
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,259 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/logging/helper'
4
+
5
+ module Legion
6
+ module Gaia
7
+ # H5 — fast per-response conflict detection (v1: correction veto only).
8
+ #
9
+ # Runs at the final-delivery hook (response_return). Deterministic pattern matching
10
+ # against high-strength correction traces and transform-tier behavioral synapses.
11
+ # No LLM calls. Must complete within the latency budget (default 50ms).
12
+ #
13
+ # The gut itself is a behavioral synapse (domain: 'gut') whose autonomy_mode gates
14
+ # what happens: observe = audit-only, filter = restraint directives, transform+ = veto.
15
+ # New partners always start in observe mode (emergent confidence 0.3 → observe tier).
16
+ module Gut
17
+ extend Legion::Logging::Helper
18
+
19
+ module_function
20
+
21
+ # Check for conflicts between draft response stats and known corrections/synapses.
22
+ #
23
+ # @param identity [String] partner identity
24
+ # @param draft_stats [Hash] stats about the draft response, e.g.
25
+ # { length_tokens:, format:, domain:, content_flags: [] }
26
+ # @return [Hash, nil]
27
+ # { conflict: true, confidence: Float, violated_trace_ids: [...], directive: Hash }
28
+ # { conflict: false }
29
+ # nil when gut check unavailable (no memory, no synapses, or latency exceeded)
30
+ def check(identity:, draft_stats:)
31
+ deadline = monotonic_now + (latency_budget_ms / 1000.0)
32
+
33
+ gut_synapse = Legion::Gaia::BehavioralSynapse.for(identity: identity.to_s, domain: 'gut')
34
+ gut_mode = gut_mode_for(gut_synapse)
35
+
36
+ return nil if gut_mode == :unavailable
37
+
38
+ violations = check_correction_traces(identity: identity.to_s, draft_stats: draft_stats,
39
+ deadline: deadline)
40
+
41
+ if violations.nil?
42
+ log.debug("[gut] latency budget exceeded identity=#{identity}")
43
+ return nil
44
+ end
45
+
46
+ synapse_violations = check_transform_synapses(identity: identity.to_s,
47
+ draft_stats: draft_stats,
48
+ deadline: deadline)
49
+
50
+ if synapse_violations.nil?
51
+ log.debug("[gut] latency budget exceeded (synapse phase) identity=#{identity}")
52
+ return nil
53
+ end
54
+
55
+ all_violations = violations + synapse_violations
56
+ return { conflict: false } if all_violations.empty?
57
+
58
+ violated_trace_ids = all_violations.flat_map { |v| v[:trace_ids] }.uniq
59
+ confidence = all_violations.map { |v| v[:strength] }.max.to_f
60
+ directive = all_violations.first[:directive] || {}
61
+
62
+ log.info("[gut] conflict detected identity=#{identity} " \
63
+ "violations=#{all_violations.size} confidence=#{confidence.round(3)} " \
64
+ "mode=#{gut_mode}")
65
+
66
+ { conflict: true, confidence: confidence, violated_trace_ids: violated_trace_ids, directive: directive }
67
+ rescue StandardError => e
68
+ handle_exception(e, level: :warn, operation: 'gaia.gut.check', identity: identity)
69
+ nil
70
+ end
71
+
72
+ def latency_budget_ms
73
+ settings = Legion::Gaia.settings
74
+ settings&.dig(:gut, :latency_budget_ms) || 50.0
75
+ end
76
+
77
+ def monotonic_now
78
+ ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
79
+ end
80
+
81
+ def deadline_exceeded?(deadline)
82
+ monotonic_now >= deadline
83
+ end
84
+
85
+ # Returns the autonomy mode for the gut synapse, or :unavailable if gut should not run.
86
+ # New partners get :observe (observe = audit only, no veto action).
87
+ def gut_mode_for(gut_synapse)
88
+ return :observe if gut_synapse.nil?
89
+
90
+ Legion::Gaia::BehavioralSynapse::Math.autonomy_mode(gut_synapse[:confidence].to_f)
91
+ end
92
+
93
+ # Compare draft_stats against high-strength correction traces for the partner.
94
+ # Returns array of violation hashes, or nil if deadline exceeded.
95
+ def check_correction_traces(identity:, draft_stats:, deadline:)
96
+ return [] unless defined?(Legion::Extensions::Agentic::Memory::Trace::Runners::Traces)
97
+
98
+ return nil if deadline_exceeded?(deadline)
99
+
100
+ runner = Object.new
101
+ runner.extend(Legion::Extensions::Agentic::Memory::Trace::Runners::Traces)
102
+
103
+ result = runner.retrieve_by_domain(
104
+ domain_tag: "partner:#{identity}",
105
+ min_strength: correction_min_strength,
106
+ limit: correction_trace_limit
107
+ )
108
+
109
+ return nil if deadline_exceeded?(deadline)
110
+
111
+ correction_traces = Array(result[:traces]).select { |t| correction_trace?(t) }
112
+ violations = []
113
+
114
+ correction_traces.each do |trace|
115
+ return nil if deadline_exceeded?(deadline)
116
+
117
+ next unless trace_conflicts_with_draft?(trace, draft_stats)
118
+
119
+ violations << {
120
+ trace_ids: [trace[:trace_id].to_s],
121
+ strength: trace[:strength].to_f,
122
+ directive: directive_from_trace(trace)
123
+ }
124
+ end
125
+
126
+ violations
127
+ rescue StandardError => e
128
+ handle_exception(e, level: :debug, operation: 'gaia.gut.check_correction_traces', identity: identity)
129
+ []
130
+ end
131
+
132
+ # Check transform-tier behavioral synapses for conflicts with draft stats.
133
+ # Returns array of violation hashes, or nil if deadline exceeded.
134
+ def check_transform_synapses(identity:, draft_stats:, deadline:)
135
+ return nil if deadline_exceeded?(deadline)
136
+
137
+ synapses = Legion::Gaia::BehavioralSynapse.all_for(identity: identity)
138
+ transform_synapses = synapses.select do |s|
139
+ mode = Legion::Gaia::BehavioralSynapse::Math.autonomy_mode(s[:confidence].to_f)
140
+ %i[transform autonomous].include?(mode)
141
+ end
142
+
143
+ violations = []
144
+
145
+ transform_synapses.each do |synapse|
146
+ return nil if deadline_exceeded?(deadline)
147
+
148
+ next unless synapse_conflicts_with_draft?(synapse, draft_stats)
149
+
150
+ violations << {
151
+ trace_ids: Array(synapse[:evidence_trace_ids]),
152
+ strength: synapse[:confidence].to_f,
153
+ directive: synapse[:directive].is_a?(Hash) ? synapse[:directive] : {}
154
+ }
155
+ end
156
+
157
+ violations
158
+ rescue StandardError => e
159
+ handle_exception(e, level: :debug, operation: 'gaia.gut.check_transform_synapses', identity: identity)
160
+ []
161
+ end
162
+
163
+ def correction_trace?(trace)
164
+ return false unless trace.is_a?(Hash)
165
+
166
+ trace[:trace_type].to_s == 'correction' ||
167
+ Array(trace[:domain_tags]).any? { |tag| tag.to_s == 'correction' }
168
+ end
169
+
170
+ # Checks if a correction trace conflicts with the draft stats.
171
+ # Conflict = the draft exhibits a pattern that the correction explicitly negated.
172
+ def trace_conflicts_with_draft?(trace, draft_stats)
173
+ payload = trace[:content_payload]
174
+ return false unless payload.is_a?(Hash)
175
+
176
+ stats = draft_stats.is_a?(Hash) ? draft_stats : {}
177
+ trace_verbosity_conflict?(payload, stats) ||
178
+ trace_format_conflict?(payload, stats) ||
179
+ trace_content_flag_conflict?(payload, stats) ||
180
+ trace_domain_conflict?(payload, stats)
181
+ end
182
+
183
+ def trace_verbosity_conflict?(payload, stats)
184
+ type = payload[:correction_type].to_s
185
+ return false unless type.include?('verbosity') || type.include?('length')
186
+
187
+ budget = payload[:budget_tokens] || payload[:max_tokens]
188
+ budget && stats[:length_tokens].to_i > budget.to_i
189
+ end
190
+
191
+ def trace_format_conflict?(payload, stats)
192
+ return false unless payload[:rejected_format]
193
+
194
+ stats[:format].to_s == payload[:rejected_format].to_s
195
+ end
196
+
197
+ def trace_content_flag_conflict?(payload, stats)
198
+ return false unless payload[:rejected_content_flags].is_a?(Array)
199
+
200
+ draft_flags = Array(stats[:content_flags]).map(&:to_s)
201
+ rejected = payload[:rejected_content_flags].map(&:to_s)
202
+ draft_flags.intersect?(rejected)
203
+ end
204
+
205
+ def trace_domain_conflict?(payload, stats)
206
+ return false unless payload[:correction_type].to_s == 'explicit_negative_feedback'
207
+
208
+ applied_domains = Array(stats[:applied_domains]).map(&:to_s)
209
+ blocked_domains = Array(payload[:blocked_domains]).map(&:to_s)
210
+ blocked_domains.any? && applied_domains.intersect?(blocked_domains)
211
+ end
212
+
213
+ # Checks if a transform-tier synapse's directive conflicts with draft stats.
214
+ def synapse_conflicts_with_draft?(synapse, draft_stats)
215
+ directive = synapse[:directive]
216
+ return false unless directive.is_a?(Hash)
217
+
218
+ stats = draft_stats.is_a?(Hash) ? draft_stats : {}
219
+ synapse_budget_conflict?(directive, stats) || synapse_format_conflict?(directive, stats)
220
+ end
221
+
222
+ def synapse_budget_conflict?(directive, stats)
223
+ return false unless directive[:budget_ratio]
224
+
225
+ baseline = stats[:baseline_tokens].to_i
226
+ draft_len = stats[:length_tokens].to_i
227
+ allowed = (baseline * directive[:budget_ratio].to_f).to_i
228
+ allowed.positive? && draft_len > allowed
229
+ end
230
+
231
+ def synapse_format_conflict?(directive, stats)
232
+ return false unless directive[:format] && stats[:format]
233
+
234
+ stats[:format].to_s != directive[:format].to_s
235
+ end
236
+
237
+ def directive_from_trace(trace)
238
+ payload = trace[:content_payload]
239
+ return {} unless payload.is_a?(Hash)
240
+
241
+ dir = {}
242
+ dir[:budget_tokens] = payload[:budget_tokens] if payload[:budget_tokens]
243
+ dir[:rejected_format] = payload[:rejected_format] if payload[:rejected_format]
244
+ dir[:correction_type] = payload[:correction_type] if payload[:correction_type]
245
+ dir
246
+ end
247
+
248
+ def correction_min_strength
249
+ settings = Legion::Gaia.settings
250
+ settings&.dig(:gut, :correction_min_strength) || 0.6
251
+ end
252
+
253
+ def correction_trace_limit
254
+ settings = Legion::Gaia.settings
255
+ settings&.dig(:gut, :correction_trace_limit) || 20
256
+ end
257
+ end
258
+ end
259
+ end
@@ -16,7 +16,9 @@ module Legion
16
16
  session: { persistence: 'auto', ttl: 86_400 },
17
17
  output: { mobile_max_length: 500, suggest_channel_switch: true },
18
18
  notifications: default_notifications,
19
- partner: default_partner
19
+ partner: default_partner,
20
+ gut: default_gut,
21
+ anticipation: default_anticipation
20
22
  }
21
23
  end
22
24
 
@@ -39,6 +41,20 @@ module Legion
39
41
  }
40
42
  end
41
43
 
44
+ def default_gut
45
+ {
46
+ latency_budget_ms: 50.0,
47
+ correction_min_strength: 0.6,
48
+ correction_trace_limit: 20
49
+ }
50
+ end
51
+
52
+ def default_anticipation
53
+ {
54
+ actionable_threshold: 0.65
55
+ }
56
+ end
57
+
42
58
  def default_notifications
43
59
  {
44
60
  enabled: true,
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module Gaia
5
- VERSION = '0.9.66'
5
+ VERSION = '0.9.67'
6
6
  end
7
7
  end
data/lib/legion/gaia.rb CHANGED
@@ -34,6 +34,8 @@ require 'legion/gaia/proactive_dispatcher'
34
34
  require 'legion/gaia/bond_registry'
35
35
  require 'legion/gaia/death_protocol'
36
36
  require 'legion/gaia/behavioral_synapse'
37
+ require 'legion/gaia/gut'
38
+ require 'legion/gaia/anticipation'
37
39
  require 'legion/gaia/partner_model'
38
40
  require 'legion/gaia/disclosure'
39
41
  require 'legion/gaia/visible_growth'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legion-gaia
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.66
4
+ version: 0.9.67
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -416,6 +416,7 @@ files:
416
416
  - lib/legion/gaia.rb
417
417
  - lib/legion/gaia/actors/heartbeat.rb
418
418
  - lib/legion/gaia/advisory.rb
419
+ - lib/legion/gaia/anticipation.rb
419
420
  - lib/legion/gaia/audit_observer.rb
420
421
  - lib/legion/gaia/behavioral_synapse.rb
421
422
  - lib/legion/gaia/bond_registry.rb
@@ -434,6 +435,7 @@ files:
434
435
  - lib/legion/gaia/cognitive_bus.rb
435
436
  - lib/legion/gaia/death_protocol.rb
436
437
  - lib/legion/gaia/disclosure.rb
438
+ - lib/legion/gaia/gut.rb
437
439
  - lib/legion/gaia/input_frame.rb
438
440
  - lib/legion/gaia/intent_classifier.rb
439
441
  - lib/legion/gaia/logging.rb