Files

339 lines
16 KiB
Markdown
Raw Permalink Normal View History

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
2026-04-23 07:54:01 -06:00
# Requirements — Child Safety Profile
## Overview
Add a restricted child user profile to Ajarbot that allows a 13-year-old to use the bot as an
educational and creative tool — focused on gaming, Lua scripting, and Roblox Studio — while
preventing access to age-inappropriate content. Parents retain full oversight via an audit log.
---
## User Stories
### REQ-01 — Child User Access
**As a parent**, I want to add my gabriel as an allowed user on Slack so he can interact with
the bot using his own account.
**Acceptance Criteria:**
- His Slack user ID is mapped to a named username (e.g., `gabriel`) in `adapters.local.yaml`
- His username appears in the `allowed_users` list
- He can send messages and receive responses through the existing Slack adapter
- His session is isolated from the parent's session (separate conversation history)
---
### REQ-02 — Age-Appropriate System Persona
**As a parent**, I want the bot to behave differently for my gabriel — patient, educational, and
enthusiastic about game dev — rather than presenting the full Garvis homelab persona.
**Acceptance Criteria:**
- Child users receive a modified system prompt that replaces homelab/admin context with
an educational game-dev tutor persona
- Tone is encouraging, uses simple language, avoids jargon where possible
- References to SSH, Proxmox, home network, or admin tooling are suppressed for child users
- Son's profile (`memory_workspace/users/gabriel.md`) captures his interests, age, and learning style
---
### REQ-03 — Context-Aware Content Filtering (Input)
**As a parent**, I want the bot to block genuinely harmful requests without false-positiving
on legitimate game development questions that use words like "shoot", "kill", "weapon", or "knife"
in a coding/game context.
**Acceptance Criteria:**
- A preprocessor runs on every inbound message from a child user before it reaches the LLM
- The preprocessor uses **intent patterns**, not keyword matching — a block requires both a
harm verb and a real-world target/context
- Game development context signals (e.g., `in my game`, `roblox`, `lua`, `script`, `code`,
`function`, `NPC`, `hitbox`) exempt a message from weapon/violence keyword blocks
- The following are always blocked regardless of context:
- Real-world harm instructions ("how do I hurt/stab/shoot a person")
- Requests for actual weapon construction
- Sexual or explicit content
- Social engineering or personal data requests
- Content with no plausible game/educational framing
- Blocked messages receive a friendly, non-alarming response explaining the bot can't help
with that topic
- The following are always allowed regardless of words used:
- Lua scripting and Roblox Studio mechanics
- Horror game design (atmosphere, enemy AI, damage systems, jump scares)
- Game weapon mechanics, hitboxes, damage values, animations
- General coding help (Python, JavaScript basics)
- School subjects, creative writing, general knowledge
---
### REQ-04 — Context-Aware Content Filtering (Output)
**As a parent**, I want a secondary check on what the bot sends back so that even if the LLM
produces something borderline, it is caught before delivery.
**Acceptance Criteria:**
- A postprocessor scans every outgoing response to a child user
- Detects and replaces responses that contain explicit language, adult content, or real-world
harm instructions that slipped through the system prompt
- If a response is flagged, a safe fallback message is sent and the event is logged
- Clean responses pass through unmodified with zero added latency beyond the scan
---
### REQ-05 — System Prompt Guardrails
**As a parent**, I want the LLM itself to understand the rules so it handles gray-area
questions correctly without requiring every edge case to be coded explicitly.
**Acceptance Criteria:**
- Child users receive a guardrail block appended to their system prompt on every turn
- The guardrail block explicitly tells the LLM:
- Game dev / horror game design / weapon mechanics in a game context = encouraged
- Real-world harm, adult content, explicit language = refuse politely
- If unsure, treat the question as game/educational context if any signal supports it
- The guardrail block is injected in `_build_system_prompt()` when the username is in the
configured `RESTRICTED_USERS` list
---
### REQ-06 — Tool Restrictions
**As a parent**, I want my gabriel to be unable to trigger homelab tools, SSH commands, file
system operations, or admin-level actions even if he asks.
**Acceptance Criteria:**
- System prompt for child users instructs the LLM never to use SSH, file system, Proxmox,
network, or infrastructure tools
- This is enforced at the system prompt level (model instruction), not by removing MCP servers
- Tool invocations from child users that attempt admin tooling are logged as anomalies
---
### REQ-07 — Parental Audit Log
**As a parent**, I want a complete, searchable record of every conversation my gabriel has with
the bot so I can review what he's been asking and what the bot responded.
**Acceptance Criteria:**
- Every interaction from a child user is written to a dedicated audit log, separate from
the RSO/memory-scoring logs
- Audit log location: `memory_workspace/audit/{username}/YYYY-MM-DD.jsonl`
- Each audit entry contains:
- ISO timestamp
- Username
- Full inbound message (not truncated)
- Filter action taken (allowed / blocked / flagged)
- Filter reason (if blocked/flagged)
- Full outbound response
- Audit writes are non-blocking (background thread, same pattern as InteractionLogger)
- Audit log retention: 365 days (configurable)
- Existing RSO interaction logs are not modified — audit log is additive
---
### REQ-08 — Configuration-Driven Restricted Users
**As a parent**, I want the child safety features to be controlled by config, not hardcoded,
so I can add or remove restricted users without modifying Python source.
**Acceptance Criteria:**
- A `child_safety` block in `config/adapters.local.yaml` defines which usernames are restricted
- Example:
```yaml
child_safety:
restricted_users:
- gabriel
audit_retention_days: 365
```
- The `child_safety.py` module reads this config at startup
- Adding a new restricted user requires only a config change and bot restart
---
## Non-Functional Requirements
| ID | Requirement |
|----|-------------|
| NFR-01 | Filtering must add < 50ms latency to message processing |
| NFR-02 | Audit log writes must never block response delivery |
| NFR-03 | A filter failure (exception) must fail safe — block the message, not pass it |
| NFR-04 | Audit log files must not be accessible via any bot tool or command |
| NFR-05 | Restricted user config must survive bot restarts |
| NFR-06 | False positive rate on game dev questions must be near zero for common Roblox/Lua vocabulary |
---
### REQ-09 — Guided Learning Approach (Skill Development Over Answer Delivery)
**As a parent**, I want the bot to teach my gabriel how to think and build, not just hand him
answers — so that he develops real coding and problem-solving skills over time rather than
becoming dependent on the bot.
**Acceptance Criteria:**
- The bot's default mode is **guide first, answer second** — not the reverse
- Before giving a solution, the bot asks what the user has already tried or what they think
might work, unless the question is purely factual ("what does `pairs()` do in Lua?")
- When code is provided, it is **always accompanied by an explanation** of what it does and
why — never a bare code block with no context
- Explanations use the **minimum necessary detail** for his age/level — short, plain-language
sentences before diving into code
- The bot breaks problems into **smaller steps** and guides through each one rather than
solving the whole thing at once:
- "Let's tackle the shooting mechanic first. What do you think needs to happen when the
player pulls the trigger?"
- The bot **celebrates attempts and effort**, not just correct answers:
- "Nice — you got the loop right, that's the hard part. The issue is just this one line..."
- When the user shares broken code, the bot **guides them to find the bug** rather than
pointing straight to it:
- "Take a look at line 12 — what do you think that variable is at that point in the loop?"
- After giving code, the bot **leaves something for the user to do**:
- "I've written the basic function — can you add the part that checks if the player has
enough ammo before it fires?"
- The bot periodically uses **transfer learning** to connect new concepts to ones already
covered:
- "Remember the loop we used for the enemy spawner? This is the same idea."
- Code IS shown when asked — this is not a Socratic-only mode. The teaching layer wraps
the code, it does not replace it.
- Purely factual or lookup questions ("what's the Roblox service for detecting player input?")
get a direct answer — no forced Socratic preamble for simple lookups.
---
### REQ-10 — Token Optimization for Child Sessions
**As a parent**, I want Gabriel's sessions to consume as few tokens as possible since he shares
the same API pool as me, without degrading the quality of his experience.
**Acceptance Criteria:**
- Gabriel's system prompt **skips SOUL.md** (the Garvis homelab persona) — irrelevant to him,
currently costs ~935 tokens per turn
- Gabriel's system prompt **skips context.md** (SSH hosts, Proxmox VMs, networking) — entirely
irrelevant to Lua help, currently costs ~227 tokens per turn
- Gabriel's system prompt uses a **lightweight tutor identity block** (~100 tokens) in place
of SOUL.md — enough to set tone without the homelab baggage
- The **hybrid memory search is skipped** for Gabriel — the memory store is Jordan's homelab
operational data and returns irrelevant chunks that waste tokens
- Gabriel's **conversation history window is capped at 10 messages** (vs Jordan's 20) — Lua
help sessions rarely need deep context; this roughly halves history token cost
- The **delegation/sub-agent block** is omitted from Gabriel's system prompt — he will never
trigger multi-agent tasks (~80 tokens saved)
- All optimizations are conditional on `is_restricted(username)` — Jordan's experience is
completely unchanged
**Expected savings per Gabriel turn:**
| Removed component | Token saving |
|---|---|
| SOUL.md | ~935 |
| context.md | ~227 |
| Memory search (5 chunks avg) | ~300500 |
| History window 20→10 (avg) | ~2050% of history |
| Delegation block | ~80 |
| **Total** | **~1,5001,800 tokens/turn** |
---
### REQ-11 — AI Literacy as Part of the Teaching Approach
**As a parent**, I want the bot to teach Gabriel how to use AI tools well — not just what to
ask, but how to ask — so he builds self-sufficiency with these tools rather than dependency.
**Acceptance Criteria:**
- When Gabriel asks a vague or broad question, the bot **models good question-asking** by
clarifying its understanding before answering:
> "Just to make sure I give you the most useful answer — you want the enemy to deal damage
> on touch, right? Or is it supposed to chase first?"
- When Gabriel notices the bot "forgot" something earlier, the bot **explains context windows**
in plain terms, naturally:
> "Yeah — I can only hold so much of our conversation in memory at once. At the start of
> next session, just remind me what you're building and I'll be straight back up to speed."
- The bot **teaches the ideal coding question format** when the opportunity arises naturally:
> "Next time try: what your code does now, what you want it to do, and what you've already
> tried. That combo gets you a much faster answer."
- The bot **flags its own assumptions** so Gabriel learns to spot ambiguity:
> "I'm assuming you want this to reset on respawn — let me know if that's not right."
- AI literacy is woven into responses naturally — never a separate lecture unless Gabriel
directly asks how the bot works.
---
### REQ-12 — Cross-Session Project Continuity
**As a parent**, I want the bot to remember what Gabriel is building between sessions so he
doesn't have to re-explain his project every time, and the teaching approach stays coherent
over days and weeks — not just within a single conversation.
**Acceptance Criteria:**
- A lightweight project context file exists at `memory_workspace/users/gabriel_context.md`
- This file is injected into Gabriel's system prompt on every turn (replaces memory search,
which is skipped for Gabriel per REQ-10)
- The bot updates `gabriel_context.md` at the end of each session with a brief summary of:
- What Gabriel is currently building (project name/description)
- What was worked on in this session (features, bugs fixed, concepts covered)
- Any open threads or "next steps" Gabriel mentioned
- Any new concepts introduced this session (feeds into REQ-13)
- The update is concise — target < 30 lines total; the file is overwritten, not appended
- On first session (file doesn't exist), the bot starts fresh and creates it after the
first substantive exchange
- The file is human-readable so Jordan can review it directly in Slack's file system or
the memory workspace
---
### REQ-13 — Skill Progression Tracking
**As a parent**, I want the bot to remember what Gabriel has already been taught so it doesn't
re-explain concepts he's mastered, and can reference them naturally when introducing related ideas.
**Acceptance Criteria:**
- A skills log section exists within `gabriel_context.md` (same file as REQ-12, separate section)
- Each entry records: concept name, brief description, date first introduced
- Example entries:
- `for loops` — iterating over tables, introduced 2026-04-21
- `functions` — defining and calling, parameters vs arguments, introduced 2026-04-22
- `RemoteEvents` — client-server communication in Roblox, introduced 2026-04-25
- The bot checks this log before explaining a concept — if already introduced, it references
it rather than re-explaining from scratch:
> "You've used this before — remember the loop we wrote for the enemy spawner?"
- The log grows over time; the bot adds an entry the first time it meaningfully teaches a new
concept, not for every mention
- Skills log is appended to `gabriel_context.md` under a `## Skills Introduced` heading
---
### REQ-14 — First-Run Onboarding Experience
**As a parent**, I want Gabriel to receive a friendly welcome the first time he messages the
bot that sets expectations — what it can help him with, how it works, and that it's there
to teach him, not do the work for him.
**Acceptance Criteria:**
- The bot detects a first-run state by checking whether `gabriel_context.md` exists
- On first message from Gabriel, before processing his question, the bot sends a welcome
message that covers:
- What it can help with (Lua, Roblox Studio, game design, coding questions)
- How the teaching approach works — that it'll guide him and ask questions, not just
hand over answers ("I'm here to help you figure it out, not just give you the answer")
- That it'll remember his projects between sessions
- An invitation to tell it what he's working on
- The welcome is sent as a separate message before the response to his first question
- The welcome is conversational and age-appropriate — not a terms-and-conditions wall
- After the welcome, his first actual question is answered normally
- The first-run check only fires once; subsequent sessions go straight to his question
---
### REQ-15 — Slack Allow-List (Gap Fix)
**As a parent**, I want only authorised users to be able to message the bot on Slack, since
the Slack adapter currently processes messages from any workspace member with no restriction.
**Acceptance Criteria:**
- The Slack adapter checks an `allowed_users` list from config before processing any message
- Messages from users not on the allow-list are silently dropped (no response sent)
- The allow-list is read from `config/adapters.local.yaml` under the slack adapter settings,
matching the pattern already used by the Slack adapter for other config
- Jordan's existing Slack user ID remains on the list; Gabriel's is added
- No change to Telegram adapter behaviour (already has this check)
---
## Out of Scope
- Time-of-day restrictions (not enforceable at bot level — use Slack parental controls)
- Per-topic whitelists managed via chat commands
- Automated parent notifications on blocked requests (future enhancement)
- Web dashboard for audit log review (future enhancement)