- 7a: Config.get_system_prompt() now logs a deprecation warning.
PersonalityManager.get_system_prompt() is the canonical source (wired
in commit 4). LLMConfig.system_prompt kept for backwards compat as
fallback when personality is not configured.
- 7b: Fix AnnouncementScheduler callback type from asyncio.coroutine
(a decorator, not a type) to Awaitable[None] from typing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 6a: Change healthcheck in Dockerfile and docker-compose.yml to verify
the PID file exists and the process is alive (kill -0) instead of
testing SQLite connectivity, which only proves the DB file exists
- 6b: In DMs, skip !commands so MeshMonitor or other bots handle them.
MeshAI only responds conversationally in DMs (no bang commands)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 5a: Import html.escape and apply to all values rendered into the
HTML template in _serve_status_page() — uptime, counts, status text,
node counts, errors. Prevents XSS via crafted node names or errors.
- 5b: Add basic prompt injection detection to _clean_query() with
configurable safety.prompt_injection_guard (default: on). Detects
patterns like "ignore all previous", "you are now", "system prompt:",
etc. Truncates query before the injection phrase and logs a warning.
Not foolproof but better than nothing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 2a: SafetyFilter + UserFilter — check user access before processing,
filter LLM responses through SafetyFilter before sending
- 2b: RateLimiter — check rate limits before processing, record
messages after successful response delivery
- 2c: PersonalityManager — pass to MessageRouter, used for system
prompt generation instead of raw config.llm.system_prompt
- 2d: WebhookClient — start/stop in lifecycle, fire events on
message_received, response_sent, error, startup, shutdown
- 2e: WebStatusServer — start/stop in lifecycle, record messages,
responses, and errors in StatusData
- 2f: AnnouncementScheduler — start/stop in lifecycle, uses
connector.send_message as callback
- 2g: FallbackBackend — wrap primary backend when config.llm.fallback
is configured, otherwise use primary directly
- 2h: CommandDispatcher — pass prefix, disabled_commands, and
custom_commands from config to create_dispatcher()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
RollingSummaryMemory now accepts a generic async summarize callable
instead of hardcoding AsyncOpenAI. Each backend provides its own
_summarize_messages() method that uses the appropriate API client.
- Removed AnthropicMemory class from anthropic_backend.py
- Removed GoogleMemory class from google_backend.py
- Changed RollingSummaryMemory.__init__ signature to accept
summarize_fn: Callable[[list[dict]], Awaitable[str]]
- All three backends (OpenAI, Anthropic, Google) now instantiate
the same RollingSummaryMemory class with backend-specific callables
- Extracted shared summarize prompt to module-level _SUMMARIZE_PROMPT
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 4a: Add threading.Lock to MeshConnector protecting _node_names and
_node_positions dicts that are read/written from Meshtastic's pubsub
thread callbacks (_on_receive, _on_node_update, _cache_node_info)
and read from async code (get_node_position, get_node_name)
- 4b: Add threading.Lock to StatusData protecting counters and activity
list that are written from the async event loop and read from the
HTTP server thread in to_dict()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 1a: Declare _position as proper dataclass field with field(default=None, init=False)
so hasattr() check isn't needed and the attribute always exists
- 1b: Load persisted conversation summaries from DB into memory cache on startup
via new _load_summaries() method called after backend creation
- 1c: Use Gemini's system_instruction parameter on GenerativeModel instead of
only prepending to first message, so system prompt persists across all turns
- 1d: Move 'import os' from line 198 to top of main.py with other imports
- 1e: Replace unreliable modulo-based cleanup timer with _last_cleanup timestamp
comparison that won't miss hours due to async sleep jitter
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use PID file to track bot process instead of pkill pattern matching
- Add 3-second debounce after restart to prevent signal storms
- Properly distinguish bot process from config tool process
- Clean up PID file on bot exit
Major expansion of the configurator TUI to support all new config options:
Main Menu:
- Reorganized into 3 tables: Core Settings (1-6), Advanced Settings (7-12), Features (13-16)
- Added options 7-16 for all new config sections
New Submenus Added:
- Rate Limits (7): messages_per_minute, global rate, cooldown, burst allowance
- Safety & Filtering (8): profanity filter, blocked phrases, require_mention, emergency keywords
- User Management (9): blocklist, allowlist, admin nodes, VIP nodes with interactive editors
- Commands (10): enable/disable, prefix, disabled commands, custom commands editor
- Personality (11): system prompt override, context injection, personas editor
- Logging (12): log level, file path, rotation settings, log verbosity toggles
- Web Status Page (14): enable, port, display options, authentication
- Announcements (15): enable, interval, channel, messages editor, random order
- Webhooks (16): enable, URL, events selection
Updated Submenus:
- History (6): Added memory settings (enabled, window_size, summarize_threshold)
All config options are now accessible through the TUI - no need to edit YAML files directly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New features:
- Rate limiting (per-user and global)
- Enhanced logging with file rotation
- LLM fallback backend support
- Safety filtering (profanity, blocked phrases, emergency keywords)
- User management (blocklist, allowlist, admin/VIP nodes)
- Custom commands with static responses
- Personality/prompt templates with persona switching
- Web status page with JSON API
- Periodic announcements/broadcasts
- Webhook integrations
New modules:
- rate_limiter.py - Per-user and global rate limiting
- safety.py - Response filtering and user access control
- personality.py - Prompt templates and persona management
- web_status.py - Simple web status dashboard
- announcements.py - Periodic broadcast scheduler
- webhook.py - Webhook notification client
- log_setup.py - Enhanced logging configuration
- backends/fallback.py - LLM fallback wrapper
Config expanded from ~50 to ~200 lines with full documentation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Avoids conflict with fq51bbs which uses 7681
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Serial vs TCP is now configured via web interface, not separate compose files.
Just uncomment devices section in docker-compose.yml if using USB serial.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Install ttyd in Docker image for browser-based TUI access
- Create docker-entrypoint.sh to run ttyd + bot with auto-restart
- Expose port 7681 for web config access
- Update docker-compose.yml with proper configuration
Access config at http://localhost:7681 after starting container
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Features:
- Multi-backend LLM support (OpenAI, Anthropic, Google)
- Rolling summary memory for token optimization (~70-80% reduction)
- Per-user conversation history with SQLite persistence
- Bang commands (!help, !ping, !reset, !status, !weather)
- Meshtastic integration via serial or TCP
- Message chunking for mesh network constraints (150 char limit)
- Rate limiting to prevent network congestion
- Rich TUI configurator
- Docker support
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>