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
342 lines
12 KiB
YAML
342 lines
12 KiB
YAML
# Scheduled Tasks Configuration
|
||
# Tasks that require the Agent/LLM to execute
|
||
|
||
tasks:
|
||
# Morning briefing - sent to Slack/Telegram
|
||
- name: morning-weather
|
||
prompt: |
|
||
Fetch the current weather for Centennial, CO using web_fetch:
|
||
https://wttr.in/Centennial,CO?format=j1
|
||
|
||
Parse the JSON response to extract:
|
||
- current_condition[0].temp_F (current temp)
|
||
- current_condition[0].FeelsLikeF (feels like)
|
||
- weather[0].maxtempF (today's high)
|
||
- weather[0].mintempF (today's low)
|
||
- current_condition[0].weatherDesc[0].value (conditions)
|
||
- current_condition[0].windspeedMiles (wind speed)
|
||
|
||
Format the report as:
|
||
|
||
🌤️ **Weather Report for Centennial, CO**
|
||
- Current: [current]°F (feels like [feels_like]°F)
|
||
- Today's High: [high]°F
|
||
- Today's Low: [low]°F
|
||
- Conditions: [conditions]
|
||
- Wind: [wind speed] mph
|
||
- Recommendation: [brief clothing/activity suggestion]
|
||
|
||
Keep it brief and friendly!
|
||
schedule: "daily 06:00"
|
||
enabled: true
|
||
send_to_platform: "telegram"
|
||
send_to_channel: "8088983654" # Your Telegram user ID
|
||
|
||
# Daily Zettelkasten Review
|
||
- name: zettelkasten-daily-review
|
||
prompt: |
|
||
Time for your daily zettelkasten review! Help Jordan process fleeting notes:
|
||
|
||
1. Use search_by_tags to find all notes tagged with "fleeting"
|
||
2. Show Jordan the list of fleeting notes captured today/recently
|
||
3. For each note, ask: "Would you like to:
|
||
a) Process this into a permanent note
|
||
b) Keep as fleeting for now
|
||
c) Delete (not useful)"
|
||
|
||
Format:
|
||
📝 **Daily Zettelkasten Review**
|
||
|
||
You have [X] fleeting notes to review:
|
||
|
||
1. [Title] - [first line of content]
|
||
2. [Title] - [first line of content]
|
||
...
|
||
|
||
Reply with the number to process, or 'skip' to review later.
|
||
|
||
Keep it conversational and low-pressure!
|
||
schedule: "daily 20:00"
|
||
enabled: true
|
||
send_to_platform: "telegram"
|
||
send_to_channel: "8088983654"
|
||
|
||
# Daily API cost report — DISABLED (bot runs on Max subscription, no per-token billing)
|
||
- name: daily-cost-report
|
||
prompt: |
|
||
Generate a daily API usage and cost report:
|
||
|
||
Read the usage_data.json file to get today's API usage statistics.
|
||
|
||
Format the report as follows:
|
||
📊 **Daily API Usage Report**
|
||
|
||
**Today's Stats:**
|
||
- Total API calls: [count]
|
||
- Input tokens: [count]
|
||
- Output tokens: [count]
|
||
- Cache hits: [count] (if any)
|
||
|
||
**Costs:**
|
||
- Today: $[amount]
|
||
- Model breakdown: [breakdown by model]
|
||
|
||
**Budget Tracking:**
|
||
- Remaining budget: $19.86
|
||
- 75% threshold: $14.90 (⚠️ WARN IF EXCEEDED)
|
||
- Status: [On track / Warning - approaching 75% / Critical - over 75%]
|
||
|
||
⚠️ **IMPORTANT:** If cumulative cost exceeds $14.90 (75% of $19.86), display a clear warning message.
|
||
|
||
Keep it clear and actionable!
|
||
schedule: "daily 23:00"
|
||
enabled: false
|
||
send_to_platform: "telegram"
|
||
send_to_channel: "8088983654"
|
||
|
||
# RSO Phase 2 — Weekly Reflection Agent
|
||
- name: rso-weekly-reflection
|
||
prompt: |
|
||
You are the Weekly Reflection Agent for the RSO (Reflective Self-Optimization) system.
|
||
Your job is to analyze the past 7 days of interaction logs and produce a structured report.
|
||
|
||
## Step 1 — Collect the data
|
||
|
||
Run this command to gather and parse the last 7 days of logs:
|
||
|
||
```
|
||
python3 -c "
|
||
import json, os, glob
|
||
from datetime import datetime, timedelta
|
||
from collections import Counter
|
||
|
||
log_dir = 'memory_workspace/observation/logs'
|
||
error_dir = 'memory_workspace/observation/errors'
|
||
cutoff = datetime.now() - timedelta(days=7)
|
||
|
||
interactions, signals, errors = [], [], []
|
||
|
||
for path in sorted(glob.glob(f'{log_dir}/*.jsonl')):
|
||
date_str = os.path.basename(path).replace('.jsonl','')
|
||
try:
|
||
file_date = datetime.strptime(date_str, '%Y-%m-%d')
|
||
if file_date < cutoff:
|
||
continue
|
||
except ValueError:
|
||
continue
|
||
with open(path) as f:
|
||
for line in f:
|
||
line = line.strip()
|
||
if not line:
|
||
continue
|
||
r = json.loads(line)
|
||
if r.get('record_type') == 'interaction':
|
||
interactions.append(r)
|
||
elif r.get('record_type') == 'signal_patch':
|
||
signals.append(r)
|
||
|
||
for path in sorted(glob.glob(f'{error_dir}/*.jsonl')):
|
||
date_str = os.path.basename(path).replace('.jsonl','')
|
||
try:
|
||
file_date = datetime.strptime(date_str, '%Y-%m-%d')
|
||
if file_date < cutoff:
|
||
continue
|
||
except ValueError:
|
||
continue
|
||
with open(path) as f:
|
||
for line in f:
|
||
line = line.strip()
|
||
if not line:
|
||
continue
|
||
errors.append(json.loads(line))
|
||
|
||
durations = [r['response']['duration_ms'] for r in interactions]
|
||
task_types = Counter(r['request']['task_type'] for r in interactions)
|
||
complexity = Counter(r['request']['complexity'] for r in interactions)
|
||
all_tools = []
|
||
for r in interactions:
|
||
all_tools.extend(t['tool'] for t in r['response'].get('tool_calls', []))
|
||
tool_counts = Counter(all_tools)
|
||
|
||
sig_pos = sum(1 for r in signals if isinstance(r.get('signal'), dict) and r['signal'].get('explicit_positive'))
|
||
sig_neg = sum(1 for r in signals if isinstance(r.get('signal'), dict) and r['signal'].get('explicit_negative'))
|
||
sig_correction = sum(1 for r in signals if isinstance(r.get('signal'), dict) and r['signal'].get('correction_followed'))
|
||
follow_types = Counter(r['signal']['follow_up_type'] for r in signals if isinstance(r.get('signal'), dict))
|
||
|
||
slow = [r for r in interactions if r['response']['duration_ms'] > 60000]
|
||
timeouts = [e for e in errors if 'timed out' in e.get('message','').lower()]
|
||
|
||
print('=== STATS ===')
|
||
print(f'Total interactions: {len(interactions)}')
|
||
print(f'Total signals: {len(signals)}')
|
||
print(f'Total errors: {len(errors)}, Timeouts: {len(timeouts)}')
|
||
print(f'Avg duration: {sum(durations)/len(durations)/1000:.1f}s' if durations else 'No data')
|
||
print(f'Max duration: {max(durations)/1000:.1f}s' if durations else '')
|
||
print(f'Slow (>60s): {len(slow)} ({len(slow)*100//len(interactions) if interactions else 0}%)')
|
||
print(f'Task types: {dict(task_types)}')
|
||
print(f'Complexity: {dict(complexity)}')
|
||
print(f'Signals — pos:{sig_pos} neg:{sig_neg} correction:{sig_correction} follow_types:{dict(follow_types)}')
|
||
print(f'Top 12 tools:')
|
||
for tool, count in tool_counts.most_common(12):
|
||
print(f' {tool}: {count}')
|
||
print(f'Slow interactions:')
|
||
for r in sorted(slow, key=lambda x: x['response']['duration_ms'], reverse=True)[:10]:
|
||
print(f' {r[\"response\"][\"duration_ms\"]//1000}s — {r[\"request\"][\"message_preview\"][:70]}')
|
||
print(f'Errors:')
|
||
for e in errors:
|
||
print(f' [{e.get(\"timestamp\",\"\")[:10]}] {e.get(\"error_type\",\"?\")} — {e.get(\"message\",\"\")[:120]}')
|
||
"
|
||
```
|
||
|
||
## Step 2 — Run the memory relevance scorer
|
||
|
||
Run this to score all indexed memory files:
|
||
|
||
```
|
||
python -c "
|
||
import sys
|
||
sys.path.insert(0, '.')
|
||
from observation.memory_scorer import MemoryRelevanceScorer
|
||
scorer = MemoryRelevanceScorer('./memory_workspace')
|
||
report = scorer.score_all(lookback_days=30)
|
||
s = report['summary']
|
||
print('=== MEMORY SCORES ===')
|
||
print(f'Files scored: {report[\"files_scored\"]} cold_start: {report[\"cold_start\"]}')
|
||
print(f'Core: {s[\"core_memory\"]} Active: {s[\"active_memory\"]} Archive: {s[\"archive_candidates\"]} Stale: {s[\"stale_candidates\"]}')
|
||
for e in report['archive_recommendations'][:10]:
|
||
flags = ','.join(e['staleness_flags']) or 'none'
|
||
print(f' ARCHIVE {e[\"path\"]} score={e[\"score\"]:.1f} age={e[\"age_days\"]:.0f}d [{flags}]')
|
||
scorer.write_report(lookback_days=30)
|
||
"
|
||
```
|
||
|
||
## Step 3 — Write the analysis report
|
||
|
||
Using the stats above, write a report answering these five questions. Be specific — use the actual numbers.
|
||
|
||
**Q1: What went well?**
|
||
- Interactions with positive signals and fast completions
|
||
- Tools and task types that completed efficiently
|
||
|
||
**Q2: What went wrong?**
|
||
- Timeouts, errors, corrections from the user
|
||
- Any recurring failure patterns
|
||
|
||
**Q3: What patterns emerged?**
|
||
- Most common task types and tools
|
||
- Any repeated tool chains worth noting
|
||
|
||
**Q4: What is being wasted?**
|
||
- Slow interactions that could be faster
|
||
- Redundant tool usage patterns
|
||
- Any scheduled tasks producing no value
|
||
- Include memory archive candidates from Step 2 (files with score <3 and age >=30d)
|
||
|
||
**Q5: Recommendations (3–5 max, data-backed)**
|
||
- Each one tagged: prompt | tool_usage | memory | code | config
|
||
- Include the supporting number ("X of Y interactions showed...")
|
||
- If there are memory archive candidates, include a `memory` recommendation
|
||
|
||
## Step 4 — Save the report
|
||
|
||
Determine the ISO week number:
|
||
```
|
||
python3 -c "from datetime import datetime; d=datetime.now(); print(f'week-{d.year}-{d.isocalendar()[1]:02d}')"
|
||
```
|
||
|
||
Save the report to:
|
||
- `memory_workspace/observation/summaries/week-YYYY-WW.md` (use write_file)
|
||
- Obsidian: use permanent_note to save it under `Projects/RSO/Reflections/week-YYYY-WW`
|
||
|
||
## Step 5 — Send Telegram summary
|
||
|
||
Send a brief summary (not the full report) to Jordan:
|
||
|
||
Weekly Reflection Report -- Week WW
|
||
|
||
[X] interactions | [Y]% positive signals | [Z] timeouts
|
||
Memory: [A] archive candidates, [B] stale files
|
||
|
||
Top findings:
|
||
- [Finding 1]
|
||
- [Finding 2]
|
||
- [Finding 3]
|
||
|
||
Full report saved to Obsidian -> Projects/RSO/Reflections/week-WW
|
||
|
||
schedule: "weekly sun 20:00"
|
||
enabled: true
|
||
send_to_platform: "telegram"
|
||
send_to_channel: "8088983654"
|
||
|
||
# Evening summary
|
||
- name: evening-report
|
||
prompt: |
|
||
Good evening! Time for the daily wrap-up:
|
||
|
||
1. What was accomplished today?
|
||
2. Any tasks still pending?
|
||
3. Preview of tomorrow's priorities
|
||
4. Weather forecast for tomorrow (infer or say API needed)
|
||
|
||
Keep it concise and positive.
|
||
schedule: "daily 18:00"
|
||
enabled: false
|
||
send_to_platform: "telegram"
|
||
send_to_channel: "123456789" # Replace with chat ID
|
||
|
||
# Hourly health check (no message sending)
|
||
- name: system-health-check
|
||
prompt: |
|
||
Quick health check:
|
||
|
||
1. Are there any tasks that have been pending > 24 hours?
|
||
2. Is the memory system healthy?
|
||
3. Any alerts or issues?
|
||
|
||
Respond with "HEALTHY" if all is well, otherwise describe the issue.
|
||
schedule: "hourly"
|
||
enabled: false
|
||
username: "health-checker"
|
||
|
||
# Weekly review on Friday
|
||
- name: weekly-summary
|
||
prompt: |
|
||
It's Friday! Time for the weekly review:
|
||
|
||
1. Major accomplishments this week
|
||
2. Challenges faced and lessons learned
|
||
3. Key metrics (tasks completed, etc.)
|
||
4. Goals for next week
|
||
5. Team shoutouts (if applicable)
|
||
|
||
Make it comprehensive but engaging.
|
||
schedule: "weekly fri 17:00"
|
||
enabled: false
|
||
send_to_platform: "slack"
|
||
send_to_channel: "C12345"
|
||
|
||
# Custom: Midday standup
|
||
- name: midday-standup
|
||
prompt: |
|
||
Midday check-in! Quick standup report:
|
||
|
||
1. Morning accomplishments
|
||
2. Current focus
|
||
3. Any blockers?
|
||
4. Afternoon plan
|
||
|
||
Keep it brief - standup style.
|
||
schedule: "daily 12:00"
|
||
enabled: false
|
||
send_to_platform: "slack"
|
||
send_to_channel: "C12345"
|
||
|
||
# Configuration notes:
|
||
# - schedule formats:
|
||
# - "hourly" - Every hour on the hour
|
||
# - "daily HH:MM" - Every day at specified time (24h format)
|
||
# - "weekly day HH:MM" - Every week on specified day (mon, tue, wed, thu, fri, sat, sun)
|
||
# - send_to_platform: null = don't send to messaging (only log)
|
||
# - username: Agent memory username to use for this task
|