diff --git a/cortex/routers/chat.py b/cortex/routers/chat.py index 69291e8..9650fe5 100644 --- a/cortex/routers/chat.py +++ b/cortex/routers/chat.py @@ -23,6 +23,7 @@ class ChatRequest(BaseModel): include_long: bool = True include_mid: bool = True include_short: bool = True + off_record: bool = False # skip session log (in-memory context preserved) user: str = "scott" persona: str = "inara" @@ -92,7 +93,8 @@ async def _stream_chat(req: ChatRequest): response_text, actual_backend = task.result() history.append({"role": "assistant", "content": response_text}) save_session(session_id, history) - log_turn(session_id, req.message, response_text) + if not req.off_record: + log_turn(session_id, req.message, response_text) requested = req.model or settings.primary_backend payload = { diff --git a/cortex/static/app.js b/cortex/static/app.js index 6181d12..dc134db 100644 --- a/cortex/static/app.js +++ b/cortex/static/app.js @@ -80,11 +80,35 @@ agentModeBtnEl.addEventListener('click', () => { agentMode = !agentMode; + if (agentMode) { otr_mode = false; update_otr_ui(); } localStorage.setItem('agentMode', agentMode); updateAgentModeUI(); inputEl.focus(); }); + // ── Off the record mode ────────────────────────────────────── + let otr_mode = false; + const otr_btn_el = document.getElementById('otr-btn'); + + function update_otr_ui() { + otr_btn_el.classList.toggle('active', otr_mode); + inputEl.classList.toggle('otr-mode', otr_mode); + updateInputPlaceholder(); + } + + otr_btn_el.addEventListener('click', () => { + otr_mode = !otr_mode; + if (otr_mode) { + agentMode = false; + noteMode = false; + localStorage.setItem('agentMode', false); + updateAgentModeUI(); + updateInputMode(); + } + update_otr_ui(); + inputEl.focus(); + }); + // ── Note mode ──────────────────────────────────────────────── let noteMode = false; let notePublic = false; @@ -124,6 +148,8 @@ inputEl.placeholder = ctrlEnterMode ? `Task for ${personaLabel}… (Gemini tool loop — Ctrl+Enter to run)` : `Task for ${personaLabel}… (Gemini tool loop)`; + } else if (otr_mode) { + inputEl.placeholder = `Off the record — not logged or distilled…`; } else { inputEl.placeholder = ctrlEnterMode ? `Message ${personaLabel}… (Ctrl+Enter to send)` @@ -133,6 +159,7 @@ noteBtnEl.addEventListener('click', () => { noteMode = !noteMode; + if (noteMode) { otr_mode = false; update_otr_ui(); } updateInputMode(); inputEl.focus(); }); @@ -690,6 +717,7 @@ include_long: memLong, include_mid: memMid, include_short: memShort, + off_record: otr_mode, user: CORTEX_USER, persona: CORTEX_PERSONA, }), diff --git a/cortex/static/index.html b/cortex/static/index.html index 6c52c50..b707335 100644 --- a/cortex/static/index.html +++ b/cortex/static/index.html @@ -131,6 +131,7 @@ + diff --git a/cortex/static/style.css b/cortex/static/style.css index 1536b8b..ed7f801 100644 --- a/cortex/static/style.css +++ b/cortex/static/style.css @@ -571,6 +571,27 @@ #note-btn.active { border-color: rgba(180, 130, 40, 0.6); color: #c9a84c; } #note-btn.active.public { border-color: rgba(40, 170, 150, 0.6); color: #4abfb0; } + /* OTR button */ + #otr-btn { + background: var(--bg); + border: 1px solid var(--border); + color: var(--muted); + border-radius: 8px; + padding: 8px 0; + cursor: pointer; + font-size: 0.85rem; + text-align: center; + transition: border-color 0.15s, color 0.15s; + } + #otr-btn:hover { border-color: var(--muted); color: var(--text); } + #otr-btn.active { border-color: rgba(120, 80, 160, 0.6); color: #a87fd4; } + + /* OTR textarea styling */ + #input.otr-mode { + border-color: rgba(120, 80, 160, 0.4); + background: rgba(120, 80, 160, 0.04); + } + /* Send button */ #send { background: var(--user-bg);