Meshview API returns {"nodes": [...]} and {"edges": [...]} wrapper
dicts, not raw lists. Added _extract_list() helper to unwrap.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds MeshAI/x.x User-Agent for good API citizenship and easier
traffic identification by Meshview/MeshMonitor operators.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements mesh intelligence with geo clustering, four-pillar health scoring,
and auto-naming regions from GPS data.
New: geo.py, mesh_health.py
Modified: config.py, main.py, router.py, configurator.py, config.example.yaml
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add MeshviewSource class for fetching nodes, edges, stats from Meshview API
- Add MeshMonitorDataSource class for fetching nodes, channels, telemetry,
traceroutes, network stats, topology, packets, solar from MeshMonitor API
- Add MeshSourceManager for managing multiple sources with aggregation
- Add MeshSourceConfig dataclass and mesh_sources list to config
- Integrate source_manager into main.py with periodic refresh
- Add source_manager parameter to MessageRouter (for future Phase 3)
- Add Mesh Sources TUI menu with add/edit/remove/test functionality
- Update config.example.yaml with mesh_sources section
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add MeshMonitor Integration section with features and configuration
- Add credit line in Features list
- Link to github.com/Yeraze/meshmonitor
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Build system prompt dynamically using bot.name and bot.owner from config
- Reorder prompt: identity -> static prompt -> MeshMonitor (conditional) -> mesh context
- MeshMonitor description only injected when meshmonitor.enabled is true
- Update default system_prompt to static parts only (commands, architecture, rules)
- Fix meshmonitor.py to handle trigger arrays (not just strings)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add MeshMonitor Sync as main menu option 9
- Toggle enabled/disabled, triggers file path, inject_into_prompt
- View Triggers shows loaded patterns from triggers.json
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add MeshMonitorSync class that reads trigger patterns from a JSON file
and compiles them to regex. The router checks incoming messages against
these patterns and ignores messages that MeshMonitor will handle.
- New meshai/meshmonitor.py: Pattern compilation and file watching
- MeshMonitorConfig dataclass with enabled, triggers_file, inject_into_prompt
- Router integration: ignore matching messages, inject commands into prompt
- Main loop refresh: watch triggers file for changes without restart
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
All three LLM backends (Google, OpenAI, Anthropic) now wrap API calls
in asyncio.wait_for() using config.timeout (default 30s). Previously
Gemini could hang indefinitely with grounding+AFC enabled.
Router catches TimeoutError with user-friendly "request timed out" message.
Empty context buffer now injects "[No recent mesh traffic observed yet.]"
so the LLM knows the capability exists even when buffer is empty.
Default system prompt updated to mention mesh awareness.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Unknown !commands (like !mail, !login, !bbs from advBBS users) were
getting "Unknown command, try !help" responses. Now returns None so
the bot stays silent on commands it doesn't recognize.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New context.py module: ring buffer (50K hard cap, ~25MB ceiling) passively
records all channel broadcasts. Observations are formatted with relative
timestamps and injected into the system prompt when generating LLM responses.
Only public channel traffic is observed; DMs to the bot are excluded (already
in per-user history). Bot's own node ID is auto-added to ignore list.
Config: context.enabled, observe_channels, ignore_nodes, max_age, max_context_items
TUI: new Context settings submenu (menu item 7)
Hourly prune removes expired observations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
MeshAI is now DM-only. Removed all unreachable channel response
paths, @mention detection, ChannelsConfig, and channel TUI menu.
Fixed restart mechanism with integrated watcher and SIGKILL fallback.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Mesh messages are short enough that summarization is unnecessary
with Gemini Flash's 1M token context window.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Delete unused CommandResult class from commands/base.py and unused
format_dm_response method from responder.py.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Removed dead menu items that referenced deleted config sections.
Renumbered menu options. Added BBS filter toggle to bot settings.
Added missing _handle_exit method.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
FallbackBackend was never configured in practice. generate_with_search
was an unused abstract method on all backends.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
These modules were wired up but never actually functional in the
running bot. Strips all imports and usage from main.py and router.py.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
These were LLM-generated planning artifacts from the memory
implementation phase. Not user-facing documentation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>