Repository navigation
Expand file tree
/
Copy pathaetherra_kernel_loop.py
More file actions
2434 lines (2211 loc) · 103 KB
/
Copy pathaetherra_kernel_loop.py
File metadata and controls
2434 lines (2211 loc) · 103 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2025 Aetherra Labs and Contributors
"""
[CORE] Aetherra OS Kernel Loop
==========================
The core heartbeat and orchestration engine for the AI-native Operating System.
This is the living brain of Aetherra - continuously processing, learning, and evolving.
"""
# Standard library imports
import asyncio
import json
import logging
import os
import secrets
import time
import uuid
from datetime import UTC, datetime, timedelta
from pathlib import Path
from typing import Any, cast
# Configure logging
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
class AetherraKernelLoop:
def _guardian_requester(self, requester: str | None = None) -> str:
principal = (
requester
or os.environ.get("AETHERRA_PRINCIPAL", "").strip()
or "kernel_loop"
)
return str(principal).strip() or "kernel_loop"
def _guardian_capability_checker(self, requester: str, capability: str) -> bool:
if requester == "kernel_loop" and capability == "kernel:control":
return True
from Aetherra.security.capabilities import has_capability
return has_capability(requester, capability)
def _guardian_preflight_control(
self,
*,
requester: str | None,
action: str,
target: str,
purpose: str,
metadata: dict[str, Any] | None = None,
) -> None:
from Aetherra.guardian import GuardianStatus, IntentDeclaration, evaluate_intent
decision = evaluate_intent(
IntentDeclaration(
requester=self._guardian_requester(requester),
subsystem="kernel",
action=action,
target=target,
purpose=purpose,
capabilities=("kernel:control",),
evidence=(f"kernel_control:{action}",),
reversible=True,
rollback_plan="restore previous kernel lifecycle or queue state",
metadata=metadata or {},
),
capability_checker=self._guardian_capability_checker,
)
if decision.status not in {
GuardianStatus.ALLOW,
GuardianStatus.ALLOW_LIMITED,
}:
raise PermissionError(f"guardian_denied:{decision.reason}:kernel:{action}")
def _get_passive_services(self) -> list[str]:
raw_list = (os.getenv("AETHERRA_PASSIVE_SERVICES", "") or "").strip()
if raw_list:
return [s.strip() for s in raw_list.split(",") if s.strip()]
return [
"memory_system",
"qfac_memory_system",
"aether_script_service",
"persistent_memory_system",
"adaptive_behavior_system",
"quantum_cognition",
"universal_cognition",
"meta_cognition",
"scheduler",
"plugin_manager",
]
"""
[BRAIN] The Core AI OS Kernel Loop
Orchestrates all system operations:
- Memory processing and optimization
- Plugin execution and coordination
- Scheduled maintenance tasks
- Real-time event handling
- System health monitoring
"""
def __init__(self, config: dict | None = None) -> None:
self.config = config or {}
self.running = False
self.start_time: datetime | None = None
self.cycle_count = 0
self.last_night_cycle: datetime | None = None
# Track a per-day key to ensure once-per-day behavior in selected TZ
self._night_last_date_key: str | None = None
self._night_cycle_scheduled = False
# Core systems (will be injected by startup)
self.memory_system: Any = None
self.plugin_manager: Any = None
self.aetherra_engine: Any = None
self.scheduler: Any = None
self.service_registry: Any = None
# Performance metrics
self.metrics = {
"total_cycles": 0,
"avg_cycle_time": 0.0,
"last_cycle_time": 0.0,
"errors_count": 0,
"night_cycles_count": 0,
# Backpressure metrics (optional bounded queues)
"drops_high": 0,
"drops_normal": 0,
"drops_background": 0,
}
# Task queues
self.high_priority_queue: asyncio.Queue[dict[str, Any]] = asyncio.Queue()
self.normal_priority_queue: asyncio.Queue[dict[str, Any]] = asyncio.Queue()
self.background_queue: asyncio.Queue[dict[str, Any]] = asyncio.Queue()
# Optional bounded queue sizes (0 = unbounded)
self.queue_limits = {
"high_priority": int(os.getenv("AETHERRA_KERNEL_QSIZE_HIGH", "0") or 0),
"normal_priority": int(os.getenv("AETHERRA_KERNEL_QSIZE_NORMAL", "0") or 0),
"background": int(os.getenv("AETHERRA_KERNEL_QSIZE_BACKGROUND", "0") or 0),
}
# Optional task persistence (best-effort snapshotting)
self.persist_tasks = os.getenv("AETHERRA_KERNEL_PERSIST_TASKS", "0") == "1"
default_tasks_path = os.path.join(
os.getenv("AETHERRA_STATE_DIR", ".aetherra"), "kernel_tasks.json"
)
self.tasks_path = Path(
os.getenv("AETHERRA_KERNEL_TASKS_PATH", default_tasks_path)
)
self._pending_tasks: dict[str, list[dict[str, Any]]] = {
"high_priority": [],
"normal_priority": [],
"background": [],
}
# Night cycle window (configurable via env)
try:
self.night_start_hour = int(os.getenv("AETHERRA_NIGHT_START_HOUR", "2"))
self.night_end_hour = int(os.getenv("AETHERRA_NIGHT_END_HOUR", "4"))
except Exception:
self.night_start_hour, self.night_end_hour = 2, 4
# Night cycle timezone control
# - AETHERRA_NIGHT_TZ: IANA name (e.g., "UTC", "America/Los_Angeles")
# - AETHERRA_NIGHT_UTC=1 pins scheduling to UTC (equivalent to AETHERRA_NIGHT_TZ=UTC)
# - AETHERRA_NIGHT_STAGGER_MAX_SEC: int seconds of max jitter to stagger start within window
self.night_tz_name = (os.getenv("AETHERRA_NIGHT_TZ", "") or "").strip()
self.night_utc = os.getenv("AETHERRA_NIGHT_UTC", "0") == "1"
try:
self.night_stagger_max_sec = int(
os.getenv("AETHERRA_NIGHT_STAGGER_MAX_SEC", "0") or 0
)
except Exception:
self.night_stagger_max_sec = 0
# Fixed jitter per process to preserve staggering consistency
self._night_stagger_sec = (
secrets.randbelow(max(1, int(self.night_stagger_max_sec)))
if self.night_stagger_max_sec > 0
else 0
)
# Task TTL (deadline) and plugin timeouts
try:
self.default_task_ttl_sec = int(
os.getenv("AETHERRA_KERNEL_TASK_DEFAULT_TTL_SEC", "0") or 0
)
except Exception:
self.default_task_ttl_sec = 0
try:
self.plugin_invoke_timeout_sec = float(
os.getenv("AETHERRA_PLUGIN_INVOKE_TIMEOUT_SEC", "30") or 30
)
except Exception:
self.plugin_invoke_timeout_sec = 30.0
# Simple circuit breaker for plugin invocations
try:
self.plugin_cb_threshold = int(
os.getenv("AETHERRA_PLUGIN_CB_THRESHOLD", "5") or 5
)
except Exception:
self.plugin_cb_threshold = 5
try:
self.plugin_cb_cooldown_sec = int(
os.getenv("AETHERRA_PLUGIN_CB_COOLDOWN_SEC", "60") or 60
)
except Exception:
self.plugin_cb_cooldown_sec = 60
self._plugin_cb_failures = 0
self._plugin_cb_open_until = 0.0
# Optional metrics flush interval (seconds) for live observability
try:
self.metrics_flush_sec = int(
os.getenv("AETHERRA_KERNEL_METRICS_FLUSH_SEC", "0") or 0
)
except Exception:
self.metrics_flush_sec = 0
self._last_metrics_flush = 0.0
# Dead Letter Queue (DLQ) for dropped/expired tasks
try:
self.dlq_enabled = os.getenv("AETHERRA_KERNEL_DLQ", "1") == "1"
default_dlq_path = os.path.join(
os.getenv("AETHERRA_STATE_DIR", ".aetherra"), "kernel_dlq.jsonl"
)
self.dlq_path = Path(
os.getenv("AETHERRA_KERNEL_DLQ_PATH", default_dlq_path)
)
self.dlq_max = int(os.getenv("AETHERRA_KERNEL_DLQ_MAX", "10000") or 10000)
except Exception:
self.dlq_enabled = True
self.dlq_path = Path(".aetherra/kernel_dlq.jsonl")
self.dlq_max = 10000
self._dlq_count = 0
# Per-plugin concurrency cap (0 = unlimited)
try:
self.plugin_max_concurrency = int(
os.getenv("AETHERRA_PLUGIN_MAX_CONCURRENCY", "0") or 0
)
except Exception:
self.plugin_max_concurrency = 0
# Optional per-capability concurrency caps via env JSON, e.g. {"network:outbound":1}
self._cap_concurrency_caps: dict[str, int] = {}
try:
# Standard library imports
import json as _json
cap_caps_raw = os.getenv("AETHERRA_PLUGIN_CAP_CONCURRENCY_MAP", "")
if cap_caps_raw:
m = _json.loads(cap_caps_raw)
if isinstance(m, dict):
self._cap_concurrency_caps = {
str(k): int(v)
for k, v in m.items()
if str(v).isdigit() or isinstance(v, int)
}
except Exception:
self._cap_concurrency_caps = {}
self._plugin_running_counts: dict[str, int] = {}
# Retry policy for transient failures (timeouts/errors)
try:
self.retry_max = int(os.getenv("AETHERRA_KERNEL_RETRY_MAX", "0") or 0)
except Exception:
self.retry_max = 0
try:
self.retry_base_delay_ms = int(
os.getenv("AETHERRA_KERNEL_RETRY_BASE_DELAY_MS", "200") or 200
)
except Exception:
self.retry_base_delay_ms = 200
# Control-plane: pause flag
self.paused = False
# Priority aging settings (seconds). 0 = disabled
try:
self.priority_aging_sec = int(
os.getenv("AETHERRA_KERNEL_AGING_SEC", "0") or 0
)
except Exception:
self.priority_aging_sec = 0
# HMR integration (metrics and control)
self._hmr_metrics: dict[str, Any] = {
"attempts": 0,
"success": 0,
"rollback": 0,
"last_swap_ms": 0,
"per_target": {},
}
self._quiesced_targets: set[str] = set()
self.hmr_controller: Any | None = None # set by launcher when HMR is enabled
# In-flight counters per target to support safer quiesce/drain
self._inflight_by_target: dict[str, int] = {}
# Optional reply waiters for synchronous result patterns
self._reply_waiters: dict[str, asyncio.Future] = {}
# Optional visible heartbeat logging (for idle visibility)
try:
self.visible_heartbeat_sec = int(
os.getenv("AETHERRA_KERNEL_HEARTBEAT_SEC", "0") or 0
)
except Exception:
self.visible_heartbeat_sec = 0
self.visible_heartbeat_level = os.getenv(
"AETHERRA_KERNEL_HEARTBEAT_LEVEL", "INFO"
).upper()
# --- Production defaults & safety rails ---
# Apply conservative defaults in production profile when not explicitly configured.
try:
profile = (os.getenv("AETHERRA_PROFILE", "") or "").strip().lower()
is_prod = profile in ("prod", "production", "staging")
# Queue size defaults (only if not explicitly set)
if is_prod:
# Detect whether operator explicitly set each limit
env_set_high = "AETHERRA_KERNEL_QSIZE_HIGH" in os.environ
env_set_norm = "AETHERRA_KERNEL_QSIZE_NORMAL" in os.environ
env_set_bg = "AETHERRA_KERNEL_QSIZE_BACKGROUND" in os.environ
# If not set or set to 0/unbounded, apply safe defaults
if (not env_set_high) or int(
self.queue_limits.get("high_priority", 0) or 0
) <= 0:
self.queue_limits["high_priority"] = 100
if (not env_set_norm) or int(
self.queue_limits.get("normal_priority", 0) or 0
) <= 0:
self.queue_limits["normal_priority"] = 500
if (not env_set_bg) or int(
self.queue_limits.get("background", 0) or 0
) <= 0:
self.queue_limits["background"] = 1000
# Ensure DLQ is enabled in production unless explicitly disabled
if "AETHERRA_KERNEL_DLQ" not in os.environ:
self.dlq_enabled = True
# Plugin timeout: cap default at 20s in production if not explicitly configured
if "AETHERRA_PLUGIN_INVOKE_TIMEOUT_SEC" not in os.environ:
try:
self.plugin_invoke_timeout_sec = min(
20.0, float(self.plugin_invoke_timeout_sec or 20.0)
)
except Exception:
self.plugin_invoke_timeout_sec = 20.0
else:
# Operator provided explicit timeout; enforce an upper safety clamp to avoid runaway long operations
try:
raw_timeout = float(
os.getenv("AETHERRA_PLUGIN_INVOKE_TIMEOUT_SEC", "0") or 0.0
)
# Hard ceiling: 120s in production; clamp and warn if exceeded
if raw_timeout > 120.0:
self.plugin_invoke_timeout_sec = 120.0
logger.warning(
f"[PROFILE][CLAMP] AETHERRA_PLUGIN_INVOKE_TIMEOUT_SEC={raw_timeout}s exceeds 120s ceiling; clamped to 120s for safety"
)
elif raw_timeout <= 0:
# Non-positive values are unsafe; reset to conservative default
self.plugin_invoke_timeout_sec = 20.0
logger.warning(
f"[PROFILE][CLAMP] Non-positive plugin timeout ({raw_timeout}); defaulting to 20s"
)
else:
# Accept operator value (still log if unusually high >60s for visibility)
self.plugin_invoke_timeout_sec = raw_timeout
if raw_timeout > 60.0:
logger.info(
f"[PROFILE] High plugin invoke timeout configured: {raw_timeout}s (<=120s ceiling)"
)
except Exception:
# Fallback to conservative default if parsing fails
self.plugin_invoke_timeout_sec = 20.0
# Circuit breaker threshold: prefer slightly lower default in production if not set
if "AETHERRA_PLUGIN_CB_THRESHOLD" not in os.environ:
try:
self.plugin_cb_threshold = max(
1, int(min(3, self.plugin_cb_threshold or 3))
)
except Exception:
self.plugin_cb_threshold = 3
# Prefer low per-plugin concurrency in prod unless operator opts in
if "AETHERRA_PLUGIN_MAX_CONCURRENCY" not in os.environ:
try:
# 0 means unlimited; default to 1 for safety
self.plugin_max_concurrency = 1
except Exception:
self.plugin_max_concurrency = 1
logger.info(
f"[PROFILE] Production defaults applied: qlimits={{high:{self.queue_limits['high_priority']}, normal:{self.queue_limits['normal_priority']}, background:{self.queue_limits['background']}}}, "
f"dlq_enabled={self.dlq_enabled}, plugin_timeout={self.plugin_invoke_timeout_sec}s, cb_threshold={self.plugin_cb_threshold}"
)
except Exception as _e:
logger.debug(f"[PROFILE] Failed to apply production defaults: {_e}")
# Initialize backpressure guard metrics placeholders
self._backpressure_guard_pass: bool | None = None
self._backpressure_guard_violations: list[str] = []
# ---- Backpressure / safety guard evaluation (P1) ----
def _evaluate_backpressure_guard(self) -> tuple[bool, list[str]]:
"""Evaluate production backpressure & plugin safety invariants.
Returns (pass, violations_list).
Skips enforcement if not production profile OR explicit override present.
Violations tracked:
- unbounded_queue_<name>
- dlq_disabled
- plugin_timeout_high (>60s)
- plugin_cb_threshold_high (>5)
- plugin_max_concurrency_unbounded (==0)
"""
profile = (os.getenv("AETHERRA_PROFILE", "") or "").strip().lower()
is_prod = profile in ("prod", "production", "staging")
override = os.getenv("AETHERRA_PROD_UNSAFE_ALLOW") or os.getenv(
"AETHERRA_ALLOW_UNBOUNDED"
)
disabled = os.getenv("AETHERRA_BACKPRESSURE_GUARD_DISABLE") == "1"
if not is_prod or override or disabled:
return True, []
violations: list[str] = []
try:
for qn, lim in self.queue_limits.items():
if int(lim or 0) <= 0:
violations.append(f"unbounded_queue_{qn}")
except Exception:
violations.append("queue_limits_parse_error")
if not getattr(self, "dlq_enabled", True):
violations.append("dlq_disabled")
try:
if float(self.plugin_invoke_timeout_sec or 0) > 60.0:
violations.append("plugin_timeout_high")
except Exception:
violations.append("plugin_timeout_parse_error")
try:
if int(self.plugin_cb_threshold or 0) > 5:
violations.append("plugin_cb_threshold_high")
except Exception:
violations.append("plugin_cb_threshold_parse_error")
try:
if int(self.plugin_max_concurrency or 0) == 0:
violations.append("plugin_max_concurrency_unbounded")
except Exception:
violations.append("plugin_max_concurrency_parse_error")
passed = len(violations) == 0
return passed, violations
# ---- Night cycle helpers (TZ-aware) ----
def _now_in_night_tz(self) -> datetime:
"""Return current time in configured night timezone (or local if unset)."""
try:
if self.night_tz_name:
try:
# Standard library imports
from zoneinfo import ZoneInfo
return datetime.now(ZoneInfo(self.night_tz_name))
except Exception:
try:
# Third party imports
import pytz # type: ignore[import-untyped]
tz = pytz.timezone(self.night_tz_name)
return datetime.now(tz)
except Exception:
return datetime.now(UTC)
if self.night_utc:
return datetime.now(UTC)
return datetime.now()
except Exception:
logger.exception("Exception occurred in _now_in_night_tz")
return datetime.now()
def _night_date_key(self, now_dt: datetime) -> str:
tz_key = self.night_tz_name or ("UTC" if self.night_utc else "local")
try:
return f"{now_dt.date().isoformat()}|{tz_key}"
except Exception:
return f"unknown|{tz_key}"
def inject_systems(
self,
memory_system: Any,
plugin_manager: Any,
aetherra_engine: Any,
scheduler: Any,
service_registry: Any,
) -> None:
"""[PLUGIN] Inject core system references for orchestration."""
self.memory_system = memory_system
self.plugin_manager = plugin_manager
self.aetherra_engine = aetherra_engine
self.scheduler = scheduler
self.service_registry = service_registry
logger.info("[LINK] Core systems injected into kernel loop")
async def _enqueue_self_incorporation_startup(self) -> None:
"""[SELFINC] Enqueue self-incorporation startup task if enabled."""
try:
# Check if self-incorporation is enabled via environment variable
enabled = os.getenv("AETHERRA_SELFINC_ENABLED", "1").strip().lower()
if enabled not in ("1", "true", "yes", "on"):
logger.info(
"[SELFINC] Self-incorporation disabled via AETHERRA_SELFINC_ENABLED"
)
return
logger.info("[SELFINC] Enqueuing self-incorporation startup task")
# Create self-incorporation service task
selfinc_task = {
"id": f"selfinc_startup_{uuid.uuid4()}",
"action": "self_incorporation_startup",
"payload": {"enabled": True},
"timestamp": time.time(),
"service": "self_incorporation",
}
# Enqueue with high priority
await self.add_task(selfinc_task, priority="high")
# Register service in registry
await self._ensure_self_incorporation_registered()
except Exception as e:
logger.error(
f"[SELFINC][FATAL] Failed to enqueue self-incorporation startup: {e}"
)
async def _ensure_self_incorporation_registered(self) -> None:
"""[SELFINC] Ensure self-incorporation service is registered in service registry."""
try:
if self.service_registry:
# Import and create the actual service instance
# Aetherra imports
from aetherra_self_incorporation import (
SelfIncorporationConfig,
SelfIncorporationService,
)
# Create service instance with default config
config = SelfIncorporationConfig()
service_instance = SelfIncorporationService(config)
# Inject kernel systems into service
service_instance.inject_systems(
self.service_registry,
self,
self.plugin_manager,
None, # agent_orchestrator not available in kernel
)
# Start the service
await service_instance.start()
# Register the actual service instance
await self.service_registry.register_service(
"self_incorporation",
service_instance,
metadata={
"version": "1.0.0",
"description": "Autonomous codebase incorporation system",
"capabilities": ["discovery", "classification", "integration"],
"self_heartbeat": True,
},
)
logger.info(
"[SELFINC] Self-incorporation service registered in service registry"
)
except Exception as e:
logger.error(f"[SELFINC][DEBUG] Service registration failed: {e}")
async def start_kernel_loop(self) -> None:
"""[LAUNCH] Start the main OS kernel loop."""
logger.info("[CORE] Starting Aetherra OS Kernel Loop...")
# Backpressure guard enforcement before declaring running
try:
passed, violations = self._evaluate_backpressure_guard()
self._backpressure_guard_pass = passed
self._backpressure_guard_violations = violations
if not passed:
logger.error(
f"[GUARD][BACKPRESSURE] Production safety guard failed: violations={violations}. Set AETHERRA_PROD_UNSAFE_ALLOW=1 only for emergency override."
)
raise RuntimeError(
f"Backpressure guard failed: {', '.join(violations)}"
)
logger.info("[GUARD][BACKPRESSURE] Production safety guard pass ✅")
except Exception:
# Re-raise to abort startup (tests rely on exception)
raise
self.running = True
self.start_time = datetime.now()
# Refuse to start in production if backpressure is unbounded (safety gate)
try:
profile = (os.getenv("AETHERRA_PROFILE", "") or "").strip().lower()
allow_unbounded = os.getenv("AETHERRA_ALLOW_UNBOUNDED", "0") == "1"
if profile in ("prod", "production", "staging") and not allow_unbounded:
ql = self.queue_limits or {}
if any(
int(ql.get(k, 0) or 0) <= 0
for k in ("high_priority", "normal_priority", "background")
):
logger.error(
"[SAFETY] Refusing to start: unbounded kernel queues in production. "
"Set AETHERRA_KERNEL_QSIZE_* > 0 or use AETHERRA_ALLOW_UNBOUNDED=1 to override."
)
raise RuntimeError("kernel_backpressure_unbounded")
# Enforce DLQ in production
if not self.dlq_enabled:
logger.error(
"[SAFETY] Refusing to start: DLQ is disabled in production. "
"Set AETHERRA_KERNEL_DLQ=1 or AETHERRA_ALLOW_UNBOUNDED=1 to override."
)
raise RuntimeError("kernel_dlq_disabled_in_production")
except Exception as _e:
# If the exception is our intentional safety exception, propagate after logging
if isinstance(_e, RuntimeError) and str(_e) in (
"kernel_backpressure_unbounded",
"kernel_dlq_disabled_in_production",
):
self.running = False
logger.critical(
f"[ABORT] Kernel did not start due to safety gate: {_e}"
)
raise
logger.debug(f"[SAFETY] Safety gate check error: {_e}")
# Load any persisted tasks (best-effort)
if self.persist_tasks:
await self._load_persisted_tasks()
# Enqueue self-incorporation startup task
await self._enqueue_self_incorporation_startup()
# Start concurrent tasks
tasks = [
asyncio.create_task(self._main_processing_loop()),
asyncio.create_task(self._background_maintenance_loop()),
asyncio.create_task(self._health_monitoring_loop()),
asyncio.create_task(self._memory_optimization_loop()),
asyncio.create_task(self._plugin_orchestration_loop()),
asyncio.create_task(self._heartbeat_loop()),
asyncio.create_task(self._passive_services_heartbeat_loop()), # new
]
# Optional visible heartbeat to keep terminal active during idle
if getattr(self, "visible_heartbeat_sec", 0) and self.visible_heartbeat_sec > 0:
tasks.append(asyncio.create_task(self._visible_heartbeat_loop()))
try:
await asyncio.gather(*tasks)
except Exception as e:
logger.error(f"[ERROR] Kernel loop error: {e}")
await self.shutdown()
async def _heartbeat_loop(self) -> None:
"""💓 Send regular heartbeat to service registry."""
while self.running:
try:
if self.service_registry:
await self.service_registry.update_heartbeat("kernel_loop")
await asyncio.sleep(60) # Heartbeat every minute
except Exception as e:
logger.error(f"[ERROR] Kernel heartbeat error: {e}")
await asyncio.sleep(60)
async def _passive_services_heartbeat_loop(self) -> None:
"""Emit periodic heartbeats for passive/core services that rarely self-report.
Many subsystems are largely event-driven and may appear "stale" even when healthy.
This lightweight loop refreshes their heartbeat at a coarse cadence to avoid
misleading degraded classifications.
Controls:
AETHERRA_PASSIVE_HEARTBEATS=0 -> disable entirely
AETHERRA_PASSIVE_HEARTBEAT_SEC -> override interval (default 90s)
AETHERRA_PASSIVE_SERVICES -> comma list of service names
"""
if os.getenv("AETHERRA_PASSIVE_HEARTBEATS", "1") != "1":
logger.debug("[PASSIVE-HB] Passive service heartbeats disabled via env")
return
try:
interval = float(os.getenv("AETHERRA_PASSIVE_HEARTBEAT_SEC", "90") or 90)
except Exception:
interval = 90.0
# Allow shorter intervals in test profile for faster feedback
profile = (os.getenv("AETHERRA_PROFILE", "") or "").strip().lower()
floor = 10 if profile in ("test", "dev") else 30
allow_floor = os.getenv("AETHERRA_PASSIVE_HEARTBEAT_ALLOW_FLOOR", "0") == "1"
if interval < floor and not allow_floor:
interval = floor
passive_services = self._get_passive_services()
while self.running and interval > 0:
try:
for svc in passive_services:
# Emit heartbeat for each passive service
if self.service_registry:
await self.service_registry.update_heartbeat(svc)
logger.debug(
f"[PASSIVE-HB] Heartbeat emitted for passive service: {svc}"
)
# Break sleep into small increments for prompt exit
slept = 0.0
sleep_step = min(0.05, interval)
while slept < interval:
try:
await asyncio.sleep(sleep_step)
except asyncio.CancelledError:
logger.debug(
"[PASSIVE-HB] Heartbeat loop cancelled during sleep."
)
return
slept += sleep_step
except Exception as e:
logger.error(
f"[PASSIVE-HB] Error emitting passive service heartbeat: {e}"
)
# Backoff a bit on error but don't spin too fast
await asyncio.sleep(min(5, interval))
async def _visible_heartbeat_loop(self) -> None:
"""Emit a lightweight heartbeat log at a configurable interval for operator visibility.
Controlled via env:
- AETHERRA_KERNEL_HEARTBEAT_SEC (int > 0 to enable)
- AETHERRA_KERNEL_HEARTBEAT_LEVEL (INFO|DEBUG; default INFO)
"""
interval = max(1, int(getattr(self, "visible_heartbeat_sec", 0) or 0))
# Map level string to logging level
level_name = (
getattr(self, "visible_heartbeat_level", "INFO") or "INFO"
).upper()
level = logging.INFO if level_name == "INFO" else logging.DEBUG
while self.running and interval > 0:
try:
upt = 0
# Standard library imports
import contextlib
with contextlib.suppress(Exception):
if self.start_time:
upt = int((datetime.now() - self.start_time).total_seconds())
qh = self.high_priority_queue.qsize()
qn = self.normal_priority_queue.qsize()
qb = self.background_queue.qsize()
avg_ms = int(
float(self.metrics.get("avg_cycle_time", 0.0) or 0.0) * 1000
)
last_ms = int(
float(self.metrics.get("last_cycle_time", 0.0) or 0.0) * 1000
)
inflight = {}
with contextlib.suppress(Exception):
inflight = self._inflight_by_target.copy()
logger.log(
level,
f"[BEAT] upt={upt}s cycles={self.cycle_count} q(H/N/B)={qh}/{qn}/{qb} avg={avg_ms}ms last={last_ms}ms paused={self.paused} inflight={inflight}",
)
await asyncio.sleep(interval)
except Exception as e:
logger.debug(f"[HB] Visible heartbeat error: {e}")
# Backoff minimally to avoid tight loop on repeated errors
await asyncio.sleep(max(5, interval))
async def _main_processing_loop(self) -> None:
"""[LOOP] Main processing cycle - handles events and orchestration."""
while self.running:
cycle_start = time.time()
try:
# Honor global pause toggle
if self.paused:
await asyncio.sleep(0.5)
continue
# Apply priority aging (promote long-waiting tasks upward)
# Standard library imports
import contextlib
with contextlib.suppress(Exception):
await self._apply_priority_aging()
# Process high priority tasks first
await self._process_task_queue(
self.high_priority_queue, "high_priority", max_tasks=5
)
# Process normal priority tasks
await self._process_task_queue(
self.normal_priority_queue, "normal_priority", max_tasks=3
)
# Process background tasks
await self._process_task_queue(
self.background_queue, "background", max_tasks=1
)
# Check for night cycle
await self._check_night_cycle()
# Update metrics
cycle_time = time.time() - cycle_start
self._update_metrics(cycle_time)
# Adaptive sleep based on load
sleep_time = max(0.1, 1.0 - cycle_time)
await asyncio.sleep(sleep_time)
except Exception as e:
logger.error(f"[ERROR] Main processing loop error: {e}")
self.metrics["errors_count"] += 1
await asyncio.sleep(1.0)
async def _background_maintenance_loop(self) -> None:
"""🛠️ Background system maintenance and optimization."""
while self.running:
try:
# Every 5 minutes: System health check
if self.cycle_count % 300 == 0:
await self._perform_health_check()
# Every 30 minutes: Memory optimization
if self.cycle_count % 1800 == 0 and self.memory_system:
await self._optimize_memory()
# Every hour: Plugin health check
if self.cycle_count % 3600 == 0 and self.plugin_manager:
await self._check_plugin_health()
# Periodic task snapshot (if enabled)
if getattr(self, "persist_tasks", False):
await self._snapshot_tasks()
# Optional periodic metrics flush for live dashboards
if self.metrics_flush_sec:
now = time.time()
if now - self._last_metrics_flush >= self.metrics_flush_sec:
await self._save_metrics()
self._last_metrics_flush = now
await asyncio.sleep(60) # Run every minute
except Exception as e:
logger.error(f"[ERROR] Background maintenance error: {e}")
await asyncio.sleep(60)
async def _health_monitoring_loop(self) -> None:
"""💓 Continuous system health monitoring."""
while self.running:
try:
# Monitor system vitals
health_status = await self._gather_health_metrics()
# Enrich payload with kernel operational telemetry
# Standard library imports
import contextlib
with contextlib.suppress(Exception):
health_status.update(
{
"queue_sizes": {
"high_priority": self.high_priority_queue.qsize(),
"normal_priority": self.normal_priority_queue.qsize(),
"background": self.background_queue.qsize(),
},
"queue_limits": getattr(self, "queue_limits", {}).copy(),
"plugin_cb_open": self._plugin_cb_is_open(),
"dlq_count": getattr(self, "_dlq_count", 0),
"last_metrics_flush": self._last_metrics_flush,
}
)
# Log health status
logger.debug(f"[HEALTH] System Health: {health_status}")
# Alert on critical issues
if health_status.get("critical_issues"):
logger.warning(
f"[WARN] Critical issues detected: {health_status['critical_issues']}"
)
# Broadcast structured health to services (best-effort)
# Standard library imports
import logging
try:
if self.service_registry and hasattr(
self.service_registry, "broadcast_message"
):
await self.service_registry.broadcast_message(
"kernel.health", health_status
)
except Exception:
logging.exception("Exception occurred during health broadcast")
await asyncio.sleep(30) # Check every 30 seconds
except Exception as e:
logger.error(f"[ERROR] Health monitoring error: {e}")
await asyncio.sleep(30)
async def _memory_optimization_loop(self) -> None:
"""[BRAIN] Continuous memory system optimization."""
while self.running:
try:
if self.memory_system:
# Light memory optimization every 10 minutes
await self.memory_system.light_optimization()
await asyncio.sleep(600) # Every 10 minutes
except Exception as e:
logger.error(f"[ERROR] Memory optimization error: {e}")
await asyncio.sleep(600)
async def _plugin_orchestration_loop(self) -> None:
"""[PLUGIN] Plugin coordination and execution."""
while self.running:
try:
if self.plugin_manager:
# Execute scheduled plugin tasks
await self.plugin_manager.execute_scheduled_tasks()
await asyncio.sleep(120) # Every 2 minutes
except Exception as e:
logger.error(f"[ERROR] Plugin orchestration error: {e}")
await asyncio.sleep(120)
async def _check_night_cycle(self) -> None:
"""🌙 Check if we should perform night cycle processing (TZ-aware, once/day)."""
# Safety: in production refuse to run if TZ not explicitly set
profile = (os.getenv("AETHERRA_PROFILE", "") or "").strip().lower()
is_prod = profile in ("prod", "production", "staging")
tz_explicit = bool(self.night_tz_name) or self.night_utc
# Track guard pass (P1 #10). Pass if not prod or TZ explicit; else fail.
try:
self._night_schedule_guard_pass = (not is_prod) or bool(tz_explicit)
except Exception:
self._night_schedule_guard_pass = False
# In prod/staging, block unconditionally if TZ not explicit to avoid surprises
if is_prod and not tz_explicit:
self.metrics["night_cycles_blocked_no_tz"] = (
self.metrics.get("night_cycles_blocked_no_tz", 0) + 1
)
logger.warning(
"[NIGHT] Blocking night cycle in production: timezone not explicitly set. "
"Set AETHERRA_NIGHT_TZ (e.g., 'UTC' or an IANA TZ) or AETHERRA_NIGHT_UTC=1."
)
return
# Determine current time in configured TZ (or local if not set)
now_tz = self._now_in_night_tz()
# Night cycle within configured window, once per day
start_h, end_h = (
getattr(self, "night_start_hour", 2),
getattr(self, "night_end_hour", 4),
)
if end_h < start_h:
end_h = start_h
in_window = start_h <= int(now_tz.hour) <= end_h
if in_window:
date_key = self._night_date_key(now_tz)
if self._night_last_date_key == date_key:
return # already handled today
# Schedule with optional staggering, capped within window length
window_len_sec = max(0, int((end_h - start_h) * 3600))
delay_sec = min(
max(0, self._night_stagger_sec), max(0, window_len_sec - 60)
)
async def _run_after_delay(delay: float, date_key_val: str) -> None:
try:
if delay > 0:
await asyncio.sleep(delay)
logger.info(
f"🌙 Initiating Night Cycle (tz={self.night_tz_name or ('UTC' if self.night_utc else 'local')}, delay={int(delay)}s)..."