Before any memory file is overwritten, _rotate_backup() keeps 2 rolling
backups: MEMORY_*.bak1.md (most recent) and MEMORY_*.bak2.md (older).
_sanity_check() now also guards against size anomalies: the new content
must be between 40% and 250% of the old file size — anything outside that
range looks like truncation or runaway output and aborts the write.
Existing checks (min length, refusal phrases) still apply.
Backup files exposed in the Files panel (ALLOWED set) so they can be
reviewed and manually restored if needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Settings page gets an editable textarea (POST /settings/email-allowlist)
so users can view and update their per-user regex allowlist without
touching the raw JSON file.
Files panel gains a "Settings" group containing email_allowlist.json as
a raw JSON editor backup — served from home/{user}/ via files.py USER_FILES.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- session_store: store sessions under home/{user}/persona/{name}/session_data/
instead of the shared cortex/data/sessions/ bucket
- chat endpoints: add user/persona query params to /sessions, /history/*,
/sessions/*, /note so they resolve the correct persona context
- files router: add user/persona query params to /files and /files/{name}
so the file browser loads the right persona's files
- app.js: pass user/persona on all session, history, and file fetches;
move _fileParams to top-level scope so it is available everywhere
- onboarding: fix FastAPI route ordering — register /persona before /{token}
so the literal path wins and does not get captured as a token value
- ui.py: read Emoji field from IDENTITY.md and inject into CORTEX_CONFIG
so the header icon reflects each persona's chosen emoji
- .gitignore: exclude home/**/session_data/ (runtime state)
- migrate scott/inara sessions from cortex/data/sessions/ to session_data/
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add cortex/persona.py: ContextVar-based per-request routing with
path traversal protection and persona validation
- Migrate inara/ → personas/inara/ (git history preserved via git mv)
- config.py: add personas_root(), inara_path() delegates to personas/inara
- All 14 settings.inara_path() call sites replaced with persona_path()
- ChatRequest + OrchestrateRequest: add persona field (default: "inara")
with validation at request entry before any processing
- memory_distiller: add optional persona param for future per-persona distill
- cron_runner/tools/cron: stamp persona on jobs, prefix APScheduler IDs
(persona:job_id) to prevent collisions across personas
- scheduler: _load_user_crons() iterates all personas at startup
Adding a new persona: create personas/<name>/ with IDENTITY.md + SOUL.md.
Auth: handled at nginx level (inject X-Cortex-Persona header per subdomain).
Future: persona maps to Aether account_id_random for full integration.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- static/index.html: reduced to 127-line HTML shell
- static/style.css: all styles extracted (~900 lines) + help modal styles
+ shared markdown rendering for file-preview and help-modal-body
including tables (previously missing)
- static/app.js: all JS extracted (~900 lines) + help modal fetch/render
- index.html: adds ? help button + help modal HTML
- inara/HELP.md: comprehensive reference doc covering all features,
keyboard shortcuts, API endpoints, memory system, planned items
- routers/files.py: HELP.md added to ALLOWED set
- context_loader.py: HELP.md loaded at tier 2+ (after PROTOCOLS.md)
so Inara can reference it when helping Scott with the interface
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>