Skip to content

feat(sessions): add configurable auto-cleanup for old agent sessions#83

Open
DeryFerd wants to merge 3 commits into
anvie:mainfrom
DeryFerd:feat/session-auto-cleanup
Open

feat(sessions): add configurable auto-cleanup for old agent sessions#83
DeryFerd wants to merge 3 commits into
anvie:mainfrom
DeryFerd:feat/session-auto-cleanup

Conversation

@DeryFerd

Copy link
Copy Markdown
Contributor

Problem

Evonic stores agent sessions indefinitely. For a long-running instance, this means the chat_sessions table and session JSONL files grow without bound. Over time, this can:

  • Slow down queries that scan the sessions table
  • Fill up disk space with old conversation logs
  • Keep personal conversation data longer than necessary

Most sessions stop being useful after a few months. Someone who tried Evonic once in January probably isn't coming back to resume that session in July. But without auto-cleanup, those dead sessions sit there forever.

What Changed

This PR adds configurable automatic cleanup of old agent sessions:

  1. New database methods (models/chat.py):

    • get_old_sessions(max_age_days) - queries sessions older than a threshold
    • delete_old_sessions(max_age_days, dry_run=False) - removes old sessions with optional dry-run mode for safety
  2. Scheduled cleanup job (backend/scheduler.py):

    • Runs daily at 04:00 (low-traffic time)
    • Uses EVONIC_SESSION_MAX_AGE_DAYS from environment (default: 90 days)
    • Cleanup can be disabled by setting EVONIC_SESSION_MAX_AGE_DAYS=0
    • Logs deleted session count for audit trail
  3. Configuration (.env.example):

    • Documents EVONIC_SESSION_MAX_AGE_DAYS with clear explanation
    • Explains the 0 = disabled behavior
    • Shows sensible default (90 days)
  4. Test coverage (unit_tests/test_session_cleanup.py):

    • Covers old session detection by timestamp threshold
    • Covers actual deletion vs. dry-run mode
    • Covers scheduled job integration with APScheduler
    • All 5 tests passing with TDD approach (red → green → refactor)

The implementation is conservative:

  • Dry-run mode lets admins preview what would be deleted
  • Default threshold (90 days) is generous
  • Admins can disable cleanup entirely
  • The scheduled job only runs once per day, not continuously

Why 90 Days?

The default is a guess. It's long enough that most active users won't lose recent sessions, but short enough to prevent unbounded growth on a busy instance.

Admins can adjust this. If you run a personal instance with one user, you might want 365 days or even 0 (disabled). If you run a shared instance with hundreds of users, 30 days might make more sense.

How It Works

The cleanup job runs at 04:00 every day (configurable in scheduler.py if needed). It:

  1. Reads EVONIC_SESSION_MAX_AGE_DAYS from environment
  2. If set to 0, skips cleanup
  3. Otherwise, calls delete_old_sessions(max_age_days)
  4. Logs how many sessions were deleted

Sessions are considered "old" based on last_updated timestamp. If a session hasn't been touched in 90+ days (or whatever threshold is configured), it gets deleted. This removes both the database row and the JSONL file.

Backward Compatibility

  • No breaking changes
  • Cleanup is opt-in via environment variable (defaults to 90 days, but admins can set 0 to disable)
  • Existing sessions are not touched until they exceed the age threshold
  • If EVONIC_SESSION_MAX_AGE_DAYS is not set, defaults to 90 days (not 0)

Testing

All tests pass:

pytest unit_tests/test_session_cleanup.py -v

Tested scenarios:

  • Creating sessions with different timestamps
  • Querying only old sessions (doesn't accidentally grab recent ones)
  • Dry-run mode (reports what would be deleted without actually deleting)
  • Actual deletion (removes old sessions and leaves recent ones intact)
  • Scheduler integration (cleanup job starts when scheduler starts)

The tests use an in-memory SQLite database and temporary JSONL files, so they don't affect real data.

Manual Testing

To test manually on a development instance:

  1. Set EVONIC_SESSION_MAX_AGE_DAYS=30 in .env
  2. Start Evonic
  3. Check logs for [SESSION_CLEANUP] messages at 04:00
  4. Or trigger immediately in Python console:
    from models.chat import cleanup_old_sessions
    cleanup_old_sessions(max_age_days=30, dry_run=True)  # Preview
    cleanup_old_sessions(max_age_days=30, dry_run=False) # Actually delete

Privacy Note

This feature helps with privacy. If someone uses Evonic once and doesn't come back, their conversation history automatically disappears after 90 days (or whatever threshold is set). That's better than keeping it forever.

For shared/public instances, this is especially useful. Old conversations from users who stopped using the system don't pile up indefinitely.

Alternative Approaches Considered

Manual cleanup script: Could work, but requires admin to remember to run it. Scheduled cleanup is more reliable.

Per-user retention settings: Adds UI complexity. Most users won't configure this. A sensible global default is simpler.

Cleanup on startup: Runs too often on frequently-restarted instances, not often enough on long-running ones. Daily scheduled cleanup is more predictable.

What Wasn't Changed

  • No changes to session creation or update logic
  • No changes to how sessions are loaded or displayed
  • No changes to the database schema
  • Scheduler initialization remains the same (cleanup job is just another registered job)

The cleanup is purely additive. If something goes wrong, setting EVONIC_SESSION_MAX_AGE_DAYS=0 disables it immediately without requiring code changes.

DeryFerd added 2 commits June 22, 2026 04:07
… Add EVONIC_PUBLIC_PROTOCOL environment variable to control protocol (http/https) embedded in downloaded Evonet connector binaries. Changes: - Add EVONIC_PUBLIC_PROTOCOL config with validation (auto/http/https) - Update protocol detection in _build_binary() with explicit override - Add debug/warning logs for protocol detection traceability - Document configuration in .env.example Motivation: When Evonic is deployed behind non-standard reverse proxies, automatic protocol detection from request headers may be unreliable. If the wrong protocol is embedded in the binary, the connector will: 1. Fail to connect (protocol mismatch) 2. Potentially expose connector_token over plaintext HTTP This fix allows explicit protocol configuration while maintaining backward-compatible auto-detection as the default. Protocol detection priority (auto mode): 1. X-Forwarded-Proto header 2. X-Real-Proto header 3. CF-Visitor header (Cloudflare) 4. request.scheme (ProxyFix-corrected) 5. Fallback to https with warning Closes issue identified in security audit: protocol misconfiguration could expose connector tokens in certain proxy configurations.
@DeryFerd DeryFerd changed the title feat(sessions): add configurable auto-cleanup for old agent sessions … feat(sessions): add configurable auto-cleanup for old agent sessions Jun 25, 2026
Add automatic cleanup of old agent sessions based on configurable age
threshold to prevent unbounded database growth.

Changes:
- models/chat.py: Add AgentChatDB.get_old_sessions(max_age_days) to
  query sessions by last-updated timestamp
- models/chat.py: Add AgentChatDB.delete_old_sessions(max_age_days, dry_run)
  that archives old sessions and their messages; dry_run=True returns count
  without making changes
- backend/scheduler.py: Add Scheduler._cleanup_old_sessions() that
  iterates all agents and calls delete_old_sessions() per agent
- backend/scheduler.py: Register sessions cleanup as a daily 04:00 cron
  job (builtin:sessions_cleanup)
- .env.example: Document EVONIC_SESSION_MAX_AGE_DAYS (default: 90 days)

Admin controls:
- Set EVONIC_SESSION_MAX_AGE_DAYS=0 to disable cleanup entirely
- Default of 90 days is conservative; adjust to your retention needs

Benefits:
- Prevents unbounded session database growth on long-running instances
- Improves query performance as session count stays bounded
- Privacy-friendly: old conversations are automatically purged
@DeryFerd DeryFerd force-pushed the feat/session-auto-cleanup branch from 4855f04 to e1c4654 Compare June 25, 2026 18:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant