feat: RSO observation system, child safety, Discord adapter, Telegram watchdog, email attachments

Core agent improvements:
- RSO (Relevance Scoring & Observation) system: interaction_logger, memory_scorer, signal_detector
- Memory access logging (memory_access_log table) for relevance scoring; high-signal turn detection
- Rich conversation storage for notable turns; compact_conversation truncates long user messages
- Task-type classifier (query/action/analysis/creative) for observation tagging
- Nested sub-agent visibility: deep delegations now register against the main agent's manager

Child safety (Gabriel profile):
- child_safety.py: filtering, audit logging, prompt constants for restricted sessions
- .kiro/specs/child-safety-profile: requirements, design, tasks specs
- GABRIEL_BOT_PROPOSAL.md: initial proposal doc
- Reduced context window (10 msgs) and tutor-mode identity for restricted users

Telegram adapter:
- Polling watchdog: auto-restarts updater if polling drops unexpectedly
- get_me() with exponential-backoff retry on NetworkError at startup
- Correct stop() ordering: signal watchdog before cancelling tasks

Email / Gmail:
- send_email: supports file attachments (attachments list param)
- get_email: surfaces attachment metadata in response

Scheduled tasks / weather:
- Remove OpenWeatherMap API calls from morning-weather task; use wttr.in exclusively
- New scheduled tasks and scheduler state persistence

Discord:
- adapters/discord/__init__.py scaffold
- discord-plugin: MCP plugin for Claude Code Discord integration (server.ts, skills, config)

Infrastructure:
- n8n workflow exports (garvis_webhook, content_pipeline variants)
- memory_workspace: context, homelab-repo-updates, weekly observation summaries, error logs
- UCS C240 migration plan doc
- requirements.txt: new deps
- .claude/settings.json, fix_hooks.py: hook/permission tuning
This commit is contained in:
2026-04-23 07:54:01 -06:00
parent 1232490c3b
commit 916f86725d
70 changed files with 10945 additions and 187 deletions

View File

@@ -112,6 +112,13 @@ class SlackAdapter(BaseAdapter):
self.is_running = False
print("[Slack] Disconnected")
def _is_user_allowed(self, user_id: str) -> bool:
"""Return False if an allow-list is configured and this user is not on it."""
allowed = self.config.settings.get("allowed_users", [])
if not allowed:
return True # open if unconfigured — preserves existing behaviour
return str(user_id) in [str(u) for u in allowed]
def _register_handlers(self) -> None:
"""Register Slack event handlers."""
@@ -121,8 +128,19 @@ class SlackAdapter(BaseAdapter):
if event.get("subtype") in ["bot_message", "message_changed"]:
return
# Suppress Slack system notifications (channel privacy changes, etc.)
raw_text = event.get("text", "")
_SUPPRESSED_PATTERNS = [
"made this channel *private*",
"has joined the channel",
]
if any(p in raw_text for p in _SUPPRESSED_PATTERNS):
return
user_id = event.get("user")
text = event.get("text", "")
if not self._is_user_allowed(user_id):
return
text = raw_text
channel = event.get("channel")
thread_ts = event.get("thread_ts")
ts = event.get("ts")
@@ -184,6 +202,8 @@ class SlackAdapter(BaseAdapter):
async def handle_app_mentions(event, say):
"""Handle @mentions of the bot."""
user_id = event.get("user")
if not self._is_user_allowed(user_id):
return
text = self._strip_mention(event.get("text", ""))
channel = event.get("channel")
thread_ts = event.get("thread_ts")