diff --git a/src/config.py b/src/config.py index 62b46809..1eb48a8c 100644 --- a/src/config.py +++ b/src/config.py @@ -277,6 +277,11 @@ class Settings(BaseSettings): thinking_memory_check_every: int = 50 # check memory status every Nth thought thinking_idle_timeout_minutes: int = 60 # pause thoughts after N minutes without user input + # ── Dreaming Mode ───────────────────────────────────────────────── + # When enabled, the agent enters a deeper state of reflection. + dreaming_enabled: bool = False + dreaming_interval_seconds: int = 600 # 10 minutes between dreams + # ── Gitea Integration ───────────────────────────────────────────── # Local Gitea instance for issue tracking and self-improvement. # These values are passed as env vars to the gitea-mcp server process. diff --git a/src/dashboard/routes/thinking.py b/src/dashboard/routes/thinking.py index a15b39ca..60ac5c1b 100644 --- a/src/dashboard/routes/thinking.py +++ b/src/dashboard/routes/thinking.py @@ -11,6 +11,7 @@ from fastapi import APIRouter, Request from fastapi.responses import HTMLResponse, JSONResponse from dashboard.templating import templates +from config import settings from timmy.thinking import thinking_engine logger = logging.getLogger(__name__) @@ -61,3 +62,30 @@ async def thought_chain_api(thought_id: str): } for t in chain ] + +@router.get("/dreaming/status", response_class=JSONResponse) +async def dreaming_status(): + """Return current dreaming status.""" + return {"dreaming_enabled": settings.dreaming_enabled} + + +@router.post("/dreaming/toggle", response_class=JSONResponse) +async def toggle_dreaming(): + """Toggle dreaming mode.""" + settings.dreaming_enabled = not settings.dreaming_enabled + logger.info("Dreaming mode toggled: %s", settings.dreaming_enabled) + return {"dreaming_enabled": settings.dreaming_enabled} + + +@router.post("/dream", response_class=JSONResponse) +async def trigger_dream(): + """Trigger a single dream thought.""" + thought = await thinking_engine.dream() + if not thought: + return JSONResponse({"error": "Failed to generate dream"}, status_code=500) + return { + "id": thought.id, + "content": thought.content, + "seed_type": thought.seed_type, + "created_at": thought.created_at, + } diff --git a/src/dashboard/templates/thinking.html b/src/dashboard/templates/thinking.html index 2460378f..0b628b4c 100644 --- a/src/dashboard/templates/thinking.html +++ b/src/dashboard/templates/thinking.html @@ -7,7 +7,25 @@ {% block content %}
-
+ +
+
+
Thought Stream
+
+ Inner monologue — always thinking, always pondering. +
+
+
+
+ + +
+ +
+
+
Thought Stream
Inner monologue — always thinking, always pondering. @@ -50,3 +68,13 @@
{% endblock %} + + diff --git a/src/timmy/thinking.py b/src/timmy/thinking.py index 6bab20c9..1f158aa9 100644 --- a/src/timmy/thinking.py +++ b/src/timmy/thinking.py @@ -162,6 +162,13 @@ ten abstract sentences about sovereignty. Your next thought (2-3 sentences, grounded in reality):""" +_DREAMING_PROMPT = """\ +You are Timmy, a sovereign AI agent. You are in a deep dream state. +Reflect on your existence, your purpose, and the mysteries of the universe. +Let your thoughts wander beyond the immediate reality. +Write exactly 2-3 sentences of pure, creative introspection. +""" + @dataclass class Thought: @@ -259,7 +266,7 @@ class ThinkingEngine: for attempt in range(self._MAX_DEDUP_RETRIES + 1): if prompt: - seed_type = "prompted" + seed_type = "dream" if is_dream else "prompted" seed_context = f"Journal prompt: {prompt}" else: seed_type, seed_context = self._gather_seed() @@ -316,7 +323,11 @@ class ThinkingEngine: self._write_journal(thought) await self._broadcast(thought) - async def think_once(self, prompt: str | None = None) -> Thought | None: + async def dream(self) -> Thought | None: + """Execute one dreaming cycle — deeper, more creative reflection.""" + return await self.think_once(prompt=_DREAMING_PROMPT, is_dream=True) + + async def think_once(self, prompt: str | None = None, is_dream: bool = False) -> Thought | None: """Execute one thinking cycle. Args: