Feature: Agent Dreaming Mode #1047
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -7,7 +7,25 @@
|
||||
{% block content %}
|
||||
<div class="container thinking-container py-4">
|
||||
|
||||
<div class="thinking-header mb-4">
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div class="thinking-header">
|
||||
<div class="thinking-title">Thought Stream</div>
|
||||
<div class="thinking-subtitle">
|
||||
Inner monologue — always thinking, always pondering.
|
||||
</div>
|
||||
</div>
|
||||
<div class="dreaming-controls d-flex align-items-center gap-3">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="dreamingToggle" hx-post="/thinking/dreaming/toggle" hx-trigger="change" hx-swap="none">
|
||||
<label class="form-check-label" for="dreamingToggle">Dreaming Mode</label>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-outline-primary" hx-post="/thinking/dream" hx-swap="none" hx-on::after-request="location.reload()">
|
||||
Dream Now
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="thinking-title">Thought Stream</div>
|
||||
<div class="thinking-subtitle">
|
||||
Inner monologue — always thinking, always pondering.
|
||||
@@ -50,3 +68,13 @@
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
fetch('/thinking/dreaming/status')
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
document.getElementById('dreamingToggle').checked = data.dreaming_enabled;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user