diff --git a/neurotron.in b/neurotron.in index 35d9002..2eed79c 100644 --- a/neurotron.in +++ b/neurotron.in @@ -4,18 +4,28 @@ PYTHON="/usr/bin/python3" NEUROTRON_HOME="/opt/kernel/neurotron" SRC="$NEUROTRON_HOME/src" -# Garante diretórios básicos +# --- Filesystems base --- mkdir -p /proc /sys /dev - -# Montar proc, sysfs e devtmpfs (idempotente, falha silenciosa se já montado) mount -t proc proc /proc 2>/dev/null || true mount -t sysfs sys /sys 2>/dev/null || true mount -t devtmpfs devtmpfs /dev 2>/dev/null || true -# Ambiente Python minimalista +# --- Ambiente --- +export HOME=/ +export TERM=linux export PYTHONHOME="/usr" export PYTHONPATH="$SRC:/usr/lib/python3.13:/usr/lib/python3.13/site-packages" -export PATH="/sbin:/bin:/usr/sbin:/usr/bin:$PATH" +export PATH="/sbin:/bin:/usr/sbin:/usr/bin" -# Arrancar o cérebro principal como módulo do package -exec "$PYTHON" -m neurotron "$@" +# --- Garante VT --- +mkdir -p /dev/pts +mount -t devpts devpts /dev/pts 2>/dev/null || true + +# --- Mudar para tty1 (VGA) --- +chvt 1 + +# --- Criar sessão e ligar ao VGA --- +exec setsid sh -c ' + exec /dev/tty1 2>&1 + exec '"$PYTHON"' -m neurotron +' diff --git a/src/neurotron/__main__.py b/src/neurotron/__main__.py index 85b9c73..b9cdf55 100644 --- a/src/neurotron/__main__.py +++ b/src/neurotron/__main__.py @@ -73,6 +73,7 @@ def main(): Path(log_dir).mkdir(parents=True, exist_ok=True) ctx = Cortex(runtime_dir=runtime_dir, log_dir=log_dir) + logbus.subscribe(ctx._on_log_event) # threads t_dash = threading.Thread(target=dashboard_loop, args=(ctx,), daemon=True) diff --git a/src/neurotron/cortex.py b/src/neurotron/cortex.py index d3b8199..d1ce27a 100644 --- a/src/neurotron/cortex.py +++ b/src/neurotron/cortex.py @@ -72,6 +72,16 @@ class Cortex: self.telemetry_path = Path(NEUROTRON_DATASET_PATH) / "telemetry.json" self.telemetry_path.parent.mkdir(parents=True, exist_ok=True) + # ---- Dashboard ---- + self.memory = Hippocampus( + log_dir=self.log_dir, + on_remember=self._on_memory_event, + ) + self.ui_bus = defaultdict(lambda: deque(maxlen=200)) + self.ui_publish("kernel", "[Placeholder (No futuro /dev/kmsg, dmesg reader ou ring buffer próprio)] ACPI: Core revision 20240415") + self.ui_publish("kernel", "[Placeholder (No futuro /dev/kmsg, dmesg reader ou ring buffer próprio)] EXT4-fs mounted filesystem") + + def _init_paths(self): """ Inicializa todos os paths canónicos do runtime Neurotron. @@ -192,6 +202,16 @@ class Cortex: return float(x) except: return fallback + + def ui_publish(self, pane: str, msg: str): + self.ui_bus[pane].append(msg) + + def ui_tail(self, pane: str, n: int): + q = self.ui_bus.get(pane) + if not q: + return [] + return list(q)[-n:] + # ---------------------------------------- # heartbeat — apenas visual, não cognitivo @@ -286,4 +306,45 @@ class Cortex: except Exception: pass - return snap \ No newline at end of file + return snap + + def _on_memory_event(self, kind: str, data: dict): + """ + Observador de eventos lembrados (UI / TRM / futuro). + """ + summary = self._summarize_memory(kind, data) + if summary: + self.ui_publish("memory", summary) + + + def _summarize_memory(self, kind: str, data: dict) -> str: + """ + Converte eventos ricos em linhas humanas (dashboard). + """ + try: + if kind == "boot": + return f"[boot] mode={data.get('mode')} tick={data.get('tick')}" + if kind.startswith("telemetry.event"): + return f"[tele] {kind.split('.')[-1]}" + if kind == "trm.snapshot": + st = data.get("state", {}) + return ( + f"[trm] cog={st.get('cog_state')} " + f"val={st.get('valence', 0):+.2f} " + f"depth={st.get('depth')}" + ) + return f"[{kind}] {str(data)[:60]}" + except Exception: + return None + + def _on_log_event(self, ev): + # sempre disponível para UI + self.ui_publish("log", ev) + + # opcional: persistência + if ev.level in ("error", "warn"): + self.memory.remember("log", { + "level": ev.level, + "msg": ev.msg + }) + diff --git a/src/neurotron/dashboard/panes/chat.py b/src/neurotron/dashboard/panes/chat.py index e6b2dbf..1d61969 100644 --- a/src/neurotron/dashboard/panes/chat.py +++ b/src/neurotron/dashboard/panes/chat.py @@ -2,4 +2,13 @@ import sys class ChatPane: def render(self, ctx, x, y, w, h): - sys.stdout.write(f"\033[{y};{x}HCHAT MESSAGES (placeholder)") \ No newline at end of file + # título + sys.stdout.write(f"\033[{y};{x}HCHAT MESSAGES:") + sys.stdout.write("\033[K") + + # consumir linhas já publicadas + lines = ctx.ui_tail("log", h-1) + for i, ev in enumerate(lines): + sys.stdout.write( + f"\033[{y+1+i};{x}H{ev.format()[:w]}" + ) \ No newline at end of file diff --git a/src/neurotron/dashboard/panes/kernel.py b/src/neurotron/dashboard/panes/kernel.py index fcbc201..9e917ee 100644 --- a/src/neurotron/dashboard/panes/kernel.py +++ b/src/neurotron/dashboard/panes/kernel.py @@ -1,6 +1,20 @@ -# panes/kernel.py +# neurotron/dashboard/panes/kernel.py import sys class KernelPane: def render(self, ctx, x, y, w, h): - sys.stdout.write(f"\033[{y};{x}HKERNEL MESSAGES (placeholder)") + # título + sys.stdout.write(f"\033[{y};{x}HKERNEL MESSAGES:") + sys.stdout.write("\033[K") + + # consumir linhas já publicadas + lines = ctx.ui_tail("kernel", h - 1) + + row = y + 1 + for line in lines: + truncated = line[:w] + sys.stdout.write(f"\033[{row};{x}H{truncated}") + sys.stdout.write("\033[K") + row += 1 + + diff --git a/src/neurotron/dashboard/panes/memory.py b/src/neurotron/dashboard/panes/memory.py index 18b1440..c716ca2 100644 --- a/src/neurotron/dashboard/panes/memory.py +++ b/src/neurotron/dashboard/panes/memory.py @@ -1,5 +1,18 @@ +# neurotron/dashboard/panes/memory.py import sys class MemoryPane: def render(self, ctx, x, y, w, h): - sys.stdout.write(f"\033[{y};{x}HMEMORY MESSAGES (placeholder)") \ No newline at end of file + # título + sys.stdout.write(f"\033[{y};{x}HMEMORY MESSAGES:") + sys.stdout.write("\033[K") + + # consumir memória recente (já filtrada!) + lines = ctx.ui_tail("memory", h - 1) + + row = y + 1 + for line in lines: + truncated = line[:w] + sys.stdout.write(f"\033[{row};{x}H{truncated}") + sys.stdout.write("\033[K") + row += 1 diff --git a/src/neurotron/dashboard/panes/trm.py b/src/neurotron/dashboard/panes/trm.py index 20f7970..cff7c56 100644 --- a/src/neurotron/dashboard/panes/trm.py +++ b/src/neurotron/dashboard/panes/trm.py @@ -2,4 +2,30 @@ import sys class TRMPane: def render(self, ctx, x, y, w, h): - sys.stdout.write(f"\033[{y};{x}HTRM MESSAGES (placeholder)") \ No newline at end of file + snap = ctx.cognitive_snapshot() + + sys.stdout.write( + f"\033[{y};{x}HTRM STATE:" + ) + + line1 = ( + f"State:{snap['cog_state']} " + f"Depth:{snap['depth']} " + f"Val:{snap['valence']:+.2f} " + f"Energy:{snap['energy']}" + ) + + sys.stdout.write(f"\033[{y+1};{x}H{line1[:w]}\033[K") + + # Separador + sys.stdout.write(f"\033[{y+2};{x}H" + "─" * w) + + # Pensamentos recentes + thoughts = ctx.ui_tail("trm", h - 3) + row = y + 3 + + for t in thoughts: + sys.stdout.write( + f"\033[{row};{x}H• {t[:w-2]}\033[K" + ) + row += 1 diff --git a/src/neurotron/hippocampus.py b/src/neurotron/hippocampus.py index a62f30a..84bbc3e 100644 --- a/src/neurotron/hippocampus.py +++ b/src/neurotron/hippocampus.py @@ -1,18 +1,24 @@ +# neurotron/hippocampus.py from pathlib import Path from datetime import datetime import json +from typing import Optional, Callable + class Hippocampus: """ Memória contextual simples (JSON Lines): append-only. - Guarda perceções, decisões e ações para replays futuros. - Apenas stdlib JSON — 100% compatível com Python estático. """ - def __init__(self, log_dir: Path): + def __init__( + self, + log_dir: Path, + on_remember: Optional[Callable[[str, dict], None]] = None, + ): self.log_dir = log_dir self.log_dir.mkdir(parents=True, exist_ok=True) self.events_file = self.log_dir / "events.jsonl" + self.on_remember = on_remember def remember(self, kind: str, data: dict) -> None: rec = { @@ -22,11 +28,15 @@ class Hippocampus: } try: - # stdlib json → sempre string - blob = json.dumps(rec, separators=(",", ":")) # compacto + blob = json.dumps(rec, separators=(",", ":")) with self.events_file.open("a", encoding="utf-8") as f: f.write(blob + "\n") except Exception: - # nunca crashar o Neurotron por logs pass + # 🔔 notificação opcional (não quebra nunca) + if self.on_remember: + try: + self.on_remember(kind, data) + except Exception: + pass diff --git a/src/neurotron/logbus.py b/src/neurotron/logbus.py index e2a2fea..a33330d 100644 --- a/src/neurotron/logbus.py +++ b/src/neurotron/logbus.py @@ -1,33 +1,39 @@ # neurotron/logbus.py -from collections import deque +from dataclasses import dataclass from datetime import datetime -import sys +@dataclass +class LogEvent: + ts: str + level: str + msg: str + + def format(self) -> str: + return f"[{self.ts}] [{self.level}] {self.msg}" class LogBus: - """ - Canal central de logs do Neurotron. - - escreve tudo em stdout (texto simples, sem Rich) - - mantém um buffer em memória para o dashboard (tail) - """ + def __init__(self): + self._subscribers = [] - def __init__(self, maxlen: int = 1000): - self._buffer = deque(maxlen=maxlen) + def subscribe(self, fn): + self._subscribers.append(fn) # ------------------------- # núcleo # ------------------------- + def _emit(self, level: str, msg: str): - ts = datetime.utcnow().strftime("%H:%M:%S") - line = f"[{ts}] [{level}] {msg}" - - # guarda em memória para o dashboard - self._buffer.append(line) - - # escreve em stdout (uma linha) - sys.stdout.write(line + "\n") - sys.stdout.flush() + ev = LogEvent( + ts=datetime.utcnow().strftime("%H:%M:%S"), + level=level, + msg=msg, + ) + for fn in self._subscribers: + try: + fn(ev) + except Exception: + pass # ------------------------- # API pública de logs @@ -61,16 +67,5 @@ class LogBus: # fallback para chamadas antigas tipo logbus.emit("...") self.info(msg) - # ------------------------- - # Para o dashboard - # ------------------------- - def tail(self, n: int = 150): - """Devolve as últimas n linhas de log como lista de strings.""" - if n <= 0: - return [] - buf = list(self._buffer) - return buf[-n:] - - # instância global -logbus = LogBus() +logbus: "LogBus" = LogBus() diff --git a/src/neurotron/trm/engine.py b/src/neurotron/trm/engine.py index 04daece..1684fef 100644 --- a/src/neurotron/trm/engine.py +++ b/src/neurotron/trm/engine.py @@ -171,6 +171,10 @@ class TRMEngine: if len(self._history) % 10 == 0 and hasattr(self.ctx, "memory"): payload = make_trm_snapshot_payload(st9, telemetry) self.ctx.memory.remember("trm.snapshot", payload) + self.ctx.ui_publish( + "memory", + f"[trm] cog={st9.cog_state} val={st9.valence:+.2f} depth={st9.depth}" + ) except Exception as e: self._dbg(f"erro ao gravar snapshot no Hippocampus: {e}") diff --git a/src/neurotron/trm/thought_agent.py b/src/neurotron/trm/thought_agent.py index 9b376a0..2d18f36 100644 --- a/src/neurotron/trm/thought_agent.py +++ b/src/neurotron/trm/thought_agent.py @@ -136,6 +136,9 @@ class ThoughtAgent: payload = {"thought": t} try: self.ctx.memory.remember("trm.thought", payload) + if hasattr(self.ctx, "ui_publish"): + self.ctx.ui_publish("trm", t) + except Exception: pass