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>
2026-03-04 12:00:49 -05:00
|
|
|
"""Telegram bot integration.
|
2026-02-22 17:16:12 +00:00
|
|
|
|
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>
2026-03-04 12:00:49 -05:00
|
|
|
Bridges Telegram messages to the local AI agent. The bot token
|
2026-02-22 17:16:12 +00:00
|
|
|
is supplied via the dashboard setup endpoint or the TELEGRAM_TOKEN env var.
|
|
|
|
|
|
|
|
|
|
Optional dependency — install with:
|
|
|
|
|
pip install ".[telegram]"
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import asyncio
|
|
|
|
|
import json
|
|
|
|
|
import logging
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
# State file lives in the project root alongside timmy.db
|
|
|
|
|
_STATE_FILE = Path(__file__).parent.parent.parent / "telegram_state.json"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _load_token_from_file() -> str | None:
|
|
|
|
|
"""Read the saved bot token from the state file."""
|
|
|
|
|
try:
|
|
|
|
|
if _STATE_FILE.exists():
|
|
|
|
|
data = json.loads(_STATE_FILE.read_text())
|
|
|
|
|
return data.get("token") or None
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
logger.debug("Could not read telegram state file: %s", exc)
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _save_token_to_file(token: str) -> None:
|
|
|
|
|
"""Persist the bot token to the state file."""
|
|
|
|
|
_STATE_FILE.write_text(json.dumps({"token": token}))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TelegramBot:
|
|
|
|
|
"""Manages the lifecycle of the python-telegram-bot Application.
|
|
|
|
|
|
|
|
|
|
Integrates with an existing asyncio event loop (e.g. FastAPI's).
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self) -> None:
|
|
|
|
|
self._app = None
|
|
|
|
|
self._token: str | None = None
|
|
|
|
|
self._running: bool = False
|
|
|
|
|
|
|
|
|
|
# ── Token helpers ─────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
def load_token(self) -> str | None:
|
|
|
|
|
"""Return the token from the state file or TELEGRAM_TOKEN env var."""
|
|
|
|
|
from_file = _load_token_from_file()
|
|
|
|
|
if from_file:
|
|
|
|
|
return from_file
|
|
|
|
|
try:
|
|
|
|
|
from config import settings
|
2026-03-08 12:50:44 -04:00
|
|
|
|
2026-02-22 17:16:12 +00:00
|
|
|
return settings.telegram_token or None
|
2026-03-14 19:07:14 -04:00
|
|
|
except Exception as exc:
|
|
|
|
|
logger.warning("Telegram token load error: %s", exc)
|
2026-02-22 17:16:12 +00:00
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def save_token(self, token: str) -> None:
|
|
|
|
|
"""Persist token so it survives restarts."""
|
|
|
|
|
_save_token_to_file(token)
|
|
|
|
|
|
|
|
|
|
# ── Status ────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def is_running(self) -> bool:
|
|
|
|
|
return self._running
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def token_set(self) -> bool:
|
|
|
|
|
return bool(self._token)
|
|
|
|
|
|
|
|
|
|
# ── Lifecycle ─────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
async def start(self, token: str | None = None) -> bool:
|
|
|
|
|
"""Start the bot. Returns True on success, False otherwise."""
|
|
|
|
|
if self._running:
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
tok = token or self.load_token()
|
|
|
|
|
if not tok:
|
|
|
|
|
logger.warning("Telegram bot: no token configured, skipping start.")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
from telegram import Update
|
|
|
|
|
from telegram.ext import (
|
|
|
|
|
Application,
|
|
|
|
|
CommandHandler,
|
|
|
|
|
MessageHandler,
|
|
|
|
|
filters,
|
|
|
|
|
)
|
|
|
|
|
except ImportError:
|
ruff (#169)
* polish: streamline nav, extract inline styles, improve tablet UX
- Restructure desktop nav from 8+ flat links + overflow dropdown into
5 grouped dropdowns (Core, Agents, Intel, System, More) matching
the mobile menu structure to reduce decision fatigue
- Extract all inline styles from mission_control.html and base.html
notification elements into mission-control.css with semantic classes
- Replace JS-built innerHTML with secure DOM construction in
notification loader and chat history
- Add CONNECTING state to connection indicator (amber) instead of
showing OFFLINE before WebSocket connects
- Add tablet breakpoint (1024px) with larger touch targets for
Apple Pencil / stylus use and safe-area padding for iPad toolbar
- Add active-link highlighting in desktop dropdown menus
- Rename "Mission Control" page title to "System Overview" to
disambiguate from the chat home page
- Add "Home — Timmy Time" page title to index.html
https://claude.ai/code/session_015uPUoKyYa8M2UAcyk5Gt6h
* fix(security): move auth-gate credentials to environment variables
Hardcoded username, password, and HMAC secret in auth-gate.py replaced
with os.environ lookups. Startup now refuses to run if any variable is
unset. Added AUTH_GATE_SECRET/USER/PASS to .env.example.
https://claude.ai/code/session_015uPUoKyYa8M2UAcyk5Gt6h
* refactor(tooling): migrate from black+isort+bandit to ruff
Replace three separate linting/formatting tools with a single ruff
invocation. Updates tox.ini (lint, format, pre-push, pre-commit envs),
.pre-commit-config.yaml, and CI workflow. Fixes all ruff errors
including unused imports, missing raise-from, and undefined names.
Ruff config maps existing bandit skips to equivalent S-rules.
https://claude.ai/code/session_015uPUoKyYa8M2UAcyk5Gt6h
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-11 12:23:35 -04:00
|
|
|
logger.error('python-telegram-bot is not installed. Run: pip install ".[telegram]"')
|
2026-02-22 17:16:12 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
self._token = tok
|
|
|
|
|
self._app = Application.builder().token(tok).build()
|
|
|
|
|
|
|
|
|
|
self._app.add_handler(CommandHandler("start", self._cmd_start))
|
|
|
|
|
self._app.add_handler(
|
|
|
|
|
MessageHandler(filters.TEXT & ~filters.COMMAND, self._handle_message)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await self._app.initialize()
|
|
|
|
|
await self._app.start()
|
|
|
|
|
await self._app.updater.start_polling(allowed_updates=Update.ALL_TYPES)
|
|
|
|
|
|
|
|
|
|
self._running = True
|
|
|
|
|
logger.info("Telegram bot started.")
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
logger.error("Telegram bot failed to start: %s", exc)
|
|
|
|
|
self._running = False
|
|
|
|
|
self._token = None
|
|
|
|
|
self._app = None
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
async def stop(self) -> None:
|
|
|
|
|
"""Gracefully shut down the bot."""
|
|
|
|
|
if not self._running or self._app is None:
|
|
|
|
|
return
|
|
|
|
|
try:
|
|
|
|
|
await self._app.updater.stop()
|
|
|
|
|
await self._app.stop()
|
|
|
|
|
await self._app.shutdown()
|
|
|
|
|
logger.info("Telegram bot stopped.")
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
logger.error("Error stopping Telegram bot: %s", exc)
|
|
|
|
|
finally:
|
|
|
|
|
self._running = False
|
|
|
|
|
|
|
|
|
|
# ── Handlers ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
async def _cmd_start(self, update, context) -> None:
|
|
|
|
|
await update.message.reply_text(
|
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>
2026-03-04 12:00:49 -05:00
|
|
|
"Local AI agent online. Send me any message and I'll get right on it."
|
2026-02-22 17:16:12 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
async def _handle_message(self, update, context) -> None:
|
|
|
|
|
user_text = update.message.text
|
|
|
|
|
try:
|
|
|
|
|
from timmy.agent import create_timmy
|
2026-03-08 12:50:44 -04:00
|
|
|
|
2026-02-22 17:16:12 +00:00
|
|
|
agent = create_timmy()
|
|
|
|
|
run = await asyncio.to_thread(agent.run, user_text, stream=False)
|
|
|
|
|
response = run.content if hasattr(run, "content") else str(run)
|
|
|
|
|
except Exception as exc:
|
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>
2026-03-04 12:00:49 -05:00
|
|
|
logger.error("Agent error in Telegram handler: %s", exc)
|
|
|
|
|
response = f"Agent is offline: {exc}"
|
2026-02-22 17:16:12 +00:00
|
|
|
await update.message.reply_text(response)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Module-level singleton
|
|
|
|
|
telegram_bot = TelegramBot()
|