feat(notes): attach selected Notes/Todos as Agent context (#6495)

* feat: add Notes and Todos as agent chat context

- Send selected Note/Todo IDs from the Notes UI
- Load selected notes with owner scoping in chat routes
- Format note content and checklist items for the model
- Inject selected notes as untrusted chat context
- Add a selected-context indicator beside Shell Access
- Keep selected context associated with the active chat session

* Improved selected notes context handling and discouragin redundant note lookup and permission requests

* Fix selected notes UI when switching chats

* fix(notes): harden selected context lifecycle and validation

---------

Co-authored-by: Alexandre Teixeira <111787685+alteixeira20@users.noreply.github.com>
This commit is contained in:
Beyrem Weslati
2026-10-13 18:57:19 +01:00
committed by GitHub
co-authored by Alexandre Teixeira
parent a8c147b238
commit 8feb50e894
9 changed files with 324 additions and 2 deletions
+15
View File
@@ -351,6 +351,9 @@ let _sessionListFocused = false;
function _deselectCurrentSession(sid) {
if (currentSessionId !== sid) return;
currentSessionId = null;
window.dispatchEvent(new CustomEvent('odysseus:session-changed', {
detail: { sessionId: null },
}));
uiModule.el('chat-history').innerHTML = '';
uiModule.el('current-meta').textContent = 'Odysseus Chat';
Storage.remove('lastSessionId');
@@ -2065,6 +2068,9 @@ export async function selectSession(id, { keepSidebar = false, showLoading = tru
try { window.documentModule.clearSelection(); } catch {}
}
currentSessionId = id;
window.dispatchEvent(new CustomEvent('odysseus:session-changed', {
detail: { sessionId: id ? id : null },
}));
try { window.__odysseusLastSelectedSessionId = id; } catch (_) {}
// Identify Assistant / task-output sessions so we don't "trap" the user
// there on return. Skipped from both `lastSessionId` persistence and the
@@ -2445,6 +2451,9 @@ export function createDirectChat(url, modelId, endpointId, opts = {}) {
_skipAutoSelect = true;
_suppressNextSessionLoading = true;
currentSessionId = null;
window.dispatchEvent(new CustomEvent('odysseus:session-changed', {
detail: { sessionId: null },
}));
try { window.__odysseusLastSelectedSessionId = ''; } catch (_) {}
Storage.remove('lastSessionId');
history.replaceState(null, '', window.location.pathname);
@@ -2555,6 +2564,9 @@ export async function materializePendingSession() {
}
_pendingChat = null;
currentSessionId = payload.id;
window.dispatchEvent(new CustomEvent('odysseus:session-changed', {
detail: { sessionId: payload.id, materialized: true },
}));
if (!isIncognito) {
Storage.set('lastSessionId', payload.id);
history.replaceState(null, '', '#' + payload.id);
@@ -2626,6 +2638,9 @@ export function getCurrentEndpointUrl() {
export function setCurrentSessionId(id) {
_sessionNavToken++;
currentSessionId = id;
window.dispatchEvent(new CustomEvent('odysseus:session-changed', {
detail: { sessionId: id ? id : null },
}));
try { window.__odysseusLastSelectedSessionId = id || ''; } catch (_) {}
if (!id) {
_suppressNextSessionLoading = true;