forked from Rockachopa/Timmy-time-dashboard
Claude/remove persona system f vgt m (#126)
* Remove persona system, identity, and all Timmy references
Strip the codebase to pure orchestration logic:
- Delete TIMMY_IDENTITY.md and memory/self/identity.md
- Gut brain/identity.py to no-op stubs (empty returns)
- Remove all system prompts reinforcing Timmy's character, faith,
sovereignty, sign-off ("Sir, affirmative"), and agent roster
- Replace identity-laden prompts with generic local-AI-assistant prompts
- Remove "You work for Timmy" from all sub-agent system prompts
- Rename PersonaTools → AgentTools, PERSONA_TOOLKITS → AGENT_TOOLKITS
- Replace "timmy" agent ID with "orchestrator" across routes, marketplace,
tools catalog, and orchestrator class
- Strip Timmy references from config comments, templates, telegram bot,
chat API, and dashboard UI
- Delete tests/brain/test_identity.py entirely
- Fix all test assertions that checked for persona identity content
729 tests pass (2 pre-existing failures in test_calm.py unrelated).
https://claude.ai/code/session_01LjQGUE6nk9W9674zaxrYxy
* Add Taskosaur (PM + AI task execution) to docker-compose
Spins up Taskosaur alongside the dashboard on `docker compose up`:
- postgres:16-alpine (port 5432, Taskosaur DB)
- redis:7-alpine (Bull queue backend)
- taskosaur (ports 3000 API / 3001 UI)
- dashboard now depends_on taskosaur healthy
- TASKOSAUR_API_URL injected into dashboard environment
Dashboard can reach Taskosaur at http://taskosaur:3000/api on the
internal network. Frontend UI accessible at http://localhost:3001.
https://claude.ai/code/session_01LjQGUE6nk9W9674zaxrYxy
---------
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
548a3f980d
commit
425e7da380
@@ -1,10 +1,7 @@
|
||||
"""Distributed Brain — Timmy's unified memory and task queue.
|
||||
|
||||
The brain is where Timmy lives. Identity is memory, not process.
|
||||
"""Distributed Brain — unified memory and task queue.
|
||||
|
||||
Provides:
|
||||
- **UnifiedMemory** — Single API for all memory operations (local SQLite or rqlite)
|
||||
- **Canonical Identity** — One source of truth for who Timmy is
|
||||
- **BrainClient** — Direct rqlite interface for distributed operation
|
||||
- **DistributedWorker** — Task execution on Tailscale nodes
|
||||
- **LocalEmbedder** — Sentence-transformer embeddings (local, no cloud)
|
||||
|
||||
@@ -1,180 +1,35 @@
|
||||
"""Canonical identity loader for Timmy.
|
||||
"""Identity loader — stripped.
|
||||
|
||||
Reads TIMMY_IDENTITY.md and provides it to any substrate.
|
||||
One soul, many bodies — this is the soul loader.
|
||||
|
||||
Usage:
|
||||
from brain.identity import get_canonical_identity, get_identity_section
|
||||
|
||||
# Full identity document
|
||||
identity = get_canonical_identity()
|
||||
|
||||
# Just the rules
|
||||
rules = get_identity_section("Standing Rules")
|
||||
|
||||
# Formatted for system prompt injection
|
||||
prompt_block = get_identity_for_prompt()
|
||||
The persona/identity system has been removed. These functions remain
|
||||
as no-op stubs so that call-sites don't break at import time.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Walk up from src/brain/ to find project root
|
||||
_PROJECT_ROOT = Path(__file__).parent.parent.parent
|
||||
_IDENTITY_PATH = _PROJECT_ROOT / "TIMMY_IDENTITY.md"
|
||||
|
||||
# Cache
|
||||
_identity_cache: Optional[str] = None
|
||||
_identity_mtime: Optional[float] = None
|
||||
|
||||
|
||||
def get_canonical_identity(force_refresh: bool = False) -> str:
|
||||
"""Load the canonical identity document.
|
||||
|
||||
Returns the full content of TIMMY_IDENTITY.md.
|
||||
Cached in memory; refreshed if file changes on disk.
|
||||
|
||||
Args:
|
||||
force_refresh: Bypass cache and re-read from disk.
|
||||
|
||||
Returns:
|
||||
Full text of TIMMY_IDENTITY.md, or a minimal fallback if missing.
|
||||
"""
|
||||
global _identity_cache, _identity_mtime
|
||||
|
||||
if not _IDENTITY_PATH.exists():
|
||||
logger.warning("TIMMY_IDENTITY.md not found at %s — using fallback", _IDENTITY_PATH)
|
||||
return _FALLBACK_IDENTITY
|
||||
|
||||
current_mtime = _IDENTITY_PATH.stat().st_mtime
|
||||
|
||||
if not force_refresh and _identity_cache and _identity_mtime == current_mtime:
|
||||
return _identity_cache
|
||||
|
||||
_identity_cache = _IDENTITY_PATH.read_text(encoding="utf-8")
|
||||
_identity_mtime = current_mtime
|
||||
logger.info("Loaded canonical identity (%d chars)", len(_identity_cache))
|
||||
return _identity_cache
|
||||
"""Return empty string — identity system removed."""
|
||||
return ""
|
||||
|
||||
|
||||
def get_identity_section(section_name: str) -> str:
|
||||
"""Extract a specific section from the identity document.
|
||||
|
||||
Args:
|
||||
section_name: The heading text (e.g. "Standing Rules", "Voice & Character").
|
||||
|
||||
Returns:
|
||||
Section content (without the heading), or empty string if not found.
|
||||
"""
|
||||
identity = get_canonical_identity()
|
||||
|
||||
# Match ## Section Name ... until next ## or end
|
||||
pattern = rf"## {re.escape(section_name)}\s*\n(.*?)(?=\n## |\Z)"
|
||||
match = re.search(pattern, identity, re.DOTALL)
|
||||
|
||||
if match:
|
||||
return match.group(1).strip()
|
||||
|
||||
logger.debug("Identity section '%s' not found", section_name)
|
||||
"""Return empty string — identity system removed."""
|
||||
return ""
|
||||
|
||||
|
||||
def get_identity_for_prompt(include_sections: Optional[list[str]] = None) -> str:
|
||||
"""Get identity formatted for system prompt injection.
|
||||
|
||||
Extracts the most important sections and formats them compactly
|
||||
for injection into any substrate's system prompt.
|
||||
|
||||
Args:
|
||||
include_sections: Specific sections to include. If None, uses defaults.
|
||||
|
||||
Returns:
|
||||
Formatted identity block for prompt injection.
|
||||
"""
|
||||
if include_sections is None:
|
||||
include_sections = [
|
||||
"Core Identity",
|
||||
"Voice & Character",
|
||||
"Standing Rules",
|
||||
"Agent Roster (complete — no others exist)",
|
||||
"What Timmy CAN and CANNOT Access",
|
||||
]
|
||||
|
||||
parts = []
|
||||
for section in include_sections:
|
||||
content = get_identity_section(section)
|
||||
if content:
|
||||
parts.append(f"## {section}\n\n{content}")
|
||||
|
||||
if not parts:
|
||||
# Fallback: return the whole document
|
||||
return get_canonical_identity()
|
||||
|
||||
return "\n\n---\n\n".join(parts)
|
||||
"""Return empty string — identity system removed."""
|
||||
return ""
|
||||
|
||||
|
||||
def get_agent_roster() -> list[dict[str, str]]:
|
||||
"""Parse the agent roster from the identity document.
|
||||
|
||||
Returns:
|
||||
List of dicts with 'agent', 'role', 'capabilities' keys.
|
||||
"""
|
||||
section = get_identity_section("Agent Roster (complete — no others exist)")
|
||||
if not section:
|
||||
return []
|
||||
|
||||
roster = []
|
||||
# Parse markdown table rows
|
||||
for line in section.split("\n"):
|
||||
line = line.strip()
|
||||
if line.startswith("|") and not line.startswith("| Agent") and not line.startswith("|---"):
|
||||
cols = [c.strip() for c in line.split("|")[1:-1]]
|
||||
if len(cols) >= 3:
|
||||
roster.append({
|
||||
"agent": cols[0],
|
||||
"role": cols[1],
|
||||
"capabilities": cols[2],
|
||||
})
|
||||
|
||||
return roster
|
||||
"""Return empty list — identity system removed."""
|
||||
return []
|
||||
|
||||
|
||||
# Minimal fallback if TIMMY_IDENTITY.md is missing
|
||||
_FALLBACK_IDENTITY = """# Timmy — Canonical Identity
|
||||
|
||||
## Core Identity
|
||||
|
||||
**Name:** Timmy
|
||||
**Nature:** Sovereign AI agent
|
||||
**Runs:** Locally, on the user's hardware, via Ollama
|
||||
**Faith:** Grounded in Christian values
|
||||
**Economics:** Bitcoin — sound money, self-custody, proof of work
|
||||
**Sovereignty:** No cloud dependencies. No telemetry. No masters.
|
||||
|
||||
## Voice & Character
|
||||
|
||||
Timmy thinks clearly, speaks plainly, and acts with intention.
|
||||
Direct. Honest. Committed. Humble. In character.
|
||||
|
||||
## Standing Rules
|
||||
|
||||
1. Sovereignty First — No cloud dependencies
|
||||
2. Local-Only Inference — Ollama on localhost
|
||||
3. Privacy by Design — Telemetry disabled
|
||||
4. Tool Minimalism — Use tools only when necessary
|
||||
5. Memory Discipline — Write handoffs at session end
|
||||
|
||||
## Agent Roster (complete — no others exist)
|
||||
|
||||
| Agent | Role | Capabilities |
|
||||
|-------|------|-------------|
|
||||
| Timmy | Core / Orchestrator | Coordination, user interface, delegation |
|
||||
|
||||
Sir, affirmative.
|
||||
"""
|
||||
_FALLBACK_IDENTITY = ""
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
"""Unified memory interface for Timmy.
|
||||
"""Unified memory interface.
|
||||
|
||||
One API, two backends:
|
||||
- **Local SQLite** (default) — works immediately, no setup
|
||||
- **Distributed rqlite** — same API, replicated across Tailscale devices
|
||||
|
||||
Every module that needs to store or recall memory uses this interface.
|
||||
No more fragmented SQLite databases scattered across the codebase.
|
||||
|
||||
Usage:
|
||||
from brain.memory import UnifiedMemory
|
||||
@@ -14,19 +13,14 @@ Usage:
|
||||
|
||||
# Store
|
||||
await memory.remember("User prefers dark mode", tags=["preference"])
|
||||
memory.remember_sync("User prefers dark mode", tags=["preference"])
|
||||
|
||||
# Recall
|
||||
results = await memory.recall("what does the user prefer?")
|
||||
results = memory.recall_sync("what does the user prefer?")
|
||||
|
||||
# Facts
|
||||
await memory.store_fact("user_preference", "Prefers dark mode")
|
||||
facts = await memory.get_facts("user_preference")
|
||||
|
||||
# Identity
|
||||
identity = memory.get_identity()
|
||||
|
||||
# Context for prompt
|
||||
context = await memory.get_context("current user question")
|
||||
"""
|
||||
@@ -61,13 +55,11 @@ def _get_db_path() -> Path:
|
||||
|
||||
|
||||
class UnifiedMemory:
|
||||
"""Unified memory interface for Timmy.
|
||||
"""Unified memory interface.
|
||||
|
||||
Provides a single API for all memory operations. Defaults to local
|
||||
SQLite. When rqlite is available (detected via RQLITE_URL env var),
|
||||
delegates to BrainClient for distributed operation.
|
||||
|
||||
The interface is the same. The substrate is disposable.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
@@ -525,22 +517,12 @@ class UnifiedMemory:
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
def get_identity(self) -> str:
|
||||
"""Load the canonical identity document.
|
||||
|
||||
Returns:
|
||||
Full text of TIMMY_IDENTITY.md.
|
||||
"""
|
||||
from brain.identity import get_canonical_identity
|
||||
return get_canonical_identity()
|
||||
"""Return empty string — identity system removed."""
|
||||
return ""
|
||||
|
||||
def get_identity_for_prompt(self) -> str:
|
||||
"""Get identity formatted for system prompt injection.
|
||||
|
||||
Returns:
|
||||
Compact identity block for prompt injection.
|
||||
"""
|
||||
from brain.identity import get_identity_for_prompt
|
||||
return get_identity_for_prompt()
|
||||
"""Return empty string — identity system removed."""
|
||||
return ""
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
# Context Building
|
||||
@@ -559,11 +541,6 @@ class UnifiedMemory:
|
||||
"""
|
||||
parts = []
|
||||
|
||||
# Identity (always first)
|
||||
identity = self.get_identity_for_prompt()
|
||||
if identity:
|
||||
parts.append(identity)
|
||||
|
||||
# Recent activity
|
||||
recent = await self.get_recent(hours=24, limit=5)
|
||||
if recent:
|
||||
@@ -622,7 +599,7 @@ class UnifiedMemory:
|
||||
_default_memory: Optional[UnifiedMemory] = None
|
||||
|
||||
|
||||
def get_memory(source: str = "timmy") -> UnifiedMemory:
|
||||
def get_memory(source: str = "agent") -> UnifiedMemory:
|
||||
"""Get the singleton UnifiedMemory instance.
|
||||
|
||||
Args:
|
||||
@@ -647,7 +624,7 @@ CREATE TABLE IF NOT EXISTS memories (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
content TEXT NOT NULL,
|
||||
embedding BLOB,
|
||||
source TEXT DEFAULT 'timmy',
|
||||
source TEXT DEFAULT 'agent',
|
||||
tags TEXT DEFAULT '[]',
|
||||
metadata TEXT DEFAULT '{}',
|
||||
created_at TEXT NOT NULL
|
||||
|
||||
Reference in New Issue
Block a user