fix: per-persona session/file isolation + onboarding route order

- 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>
This commit is contained in:
Scott Idem
2026-03-22 00:01:07 -04:00
parent 99f8961bec
commit c01ef663f5
8 changed files with 197 additions and 187 deletions

View File

@@ -17,6 +17,10 @@
// User/persona injected by the server at /{user}/{persona}
const CORTEX_USER = (window.CORTEX_CONFIG || {}).user || 'scott';
const CORTEX_PERSONA = (window.CORTEX_CONFIG || {}).persona || 'inara';
const CORTEX_EMOJI = (window.CORTEX_CONFIG || {}).emoji || '✨';
const _fileParams = `user=${encodeURIComponent(CORTEX_USER)}&persona=${encodeURIComponent(CORTEX_PERSONA)}`;
if (headerEmoji) headerEmoji.textContent = CORTEX_EMOJI;
let sessionId = null;
let primaryBackend = 'claude';
@@ -219,7 +223,7 @@
sessionsPanel.classList.remove('open');
return;
}
const res = await fetch('/sessions');
const res = await fetch(`/sessions?${_fileParams}`);
const data = await res.json();
renderPanel(data.sessions);
sessionsPanel.classList.add('open');
@@ -268,7 +272,7 @@
delBtn.title = 'Delete session';
delBtn.addEventListener('click', async (e) => {
e.stopPropagation();
await fetch(`/sessions/${s.session_id}`, { method: 'DELETE' });
await fetch(`/sessions/${s.session_id}?${_fileParams}`, { method: 'DELETE' });
if (sessionId === s.session_id) {
sessionId = null;
currentHistory = [];
@@ -276,7 +280,7 @@
sessionEl.textContent = '';
addMessage('system', 'Session deleted');
}
const res = await fetch('/sessions');
const res = await fetch(`/sessions?${_fileParams}`);
const data = await res.json();
renderPanel(data.sessions);
});
@@ -307,7 +311,7 @@
async function resumeSession(id) {
talkThinkingDiv = null;
if (id && id.startsWith('nct_')) sessionsBtn.classList.remove('talk-badge');
const res = await fetch(`/history/${id}`);
const res = await fetch(`/history/${id}?${_fileParams}`);
const data = await res.json();
messagesEl.innerHTML = '';
@@ -524,7 +528,7 @@
async function syncHistory() {
if (!sessionId) return;
try {
await fetch(`/history/${sessionId}`, {
await fetch(`/history/${sessionId}?${_fileParams}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ messages: currentHistory }),
@@ -856,7 +860,7 @@
}
async function loadFile(name) {
const res = await fetch(`/files/${encodeURIComponent(name)}`);
const res = await fetch(`/files/${encodeURIComponent(name)}?${_fileParams}`);
if (!res.ok) { fileEditor.value = `Error loading ${name}`; return; }
const data = await res.json();
fileEditor.value = data.content;
@@ -866,7 +870,7 @@
async function openFileModal() {
// Populate the file list
const res = await fetch('/files');
const res = await fetch(`/files?${_fileParams}`);
const data = await res.json();
fileSelect.innerHTML = '';
for (const f of data.files) {
@@ -888,7 +892,7 @@
fileSaveBtn.addEventListener('click', async () => {
const name = fileSelect.value;
const res = await fetch(`/files/${encodeURIComponent(name)}`, {
const res = await fetch(`/files/${encodeURIComponent(name)}?${_fileParams}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content: fileEditor.value }),
@@ -1154,7 +1158,7 @@
helpBody.textContent = 'Loading…';
helpModal.classList.add('open');
try {
const res = await fetch('/files/HELP.md');
const res = await fetch(`/files/HELP.md?${_fileParams}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
helpBody.innerHTML = marked.parse(data.content);