feat(ui): hide observer-sessions project from UI lists

Observer sessions (internal SDK-driven worker queries) run under a
synthetic project name 'observer-sessions' to keep them out of
claude --resume. They were still surfacing in the viewer project
picker and unfiltered observation/summary/prompt feeds.

Filter them out at every UI-facing query:
- SessionStore.getAllProjects and getProjectCatalog
- timeline/queries.ts getAllProjects
- PaginationHelper observations/summaries/prompts when no project is selected

When a caller explicitly requests project='observer-sessions',
results are still returned (not a hard ban, just hidden by default).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alex Newman
2026-04-16 20:05:37 -07:00
parent f6fda8fff4
commit d1601123fd
7 changed files with 116 additions and 96 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
View File
@@ -1192,6 +1192,7 @@ export class SessionStore {
SELECT DISTINCT project
FROM sdk_sessions
WHERE project IS NOT NULL AND project != ''
AND project != 'observer-sessions'
`;
const params: unknown[] = [];
@@ -1218,6 +1219,7 @@ export class SessionStore {
MAX(started_at_epoch) as latest_epoch
FROM sdk_sessions
WHERE project IS NOT NULL AND project != ''
AND project != 'observer-sessions'
GROUP BY COALESCE(platform_source, '${DEFAULT_PLATFORM_SOURCE}'), project
ORDER BY latest_epoch DESC
`).all() as Array<{ platform_source: string; project: string; latest_epoch: number }>;
+1
View File
@@ -210,6 +210,7 @@ export function getAllProjects(db: Database): string[] {
SELECT DISTINCT project
FROM sdk_sessions
WHERE project IS NOT NULL AND project != ''
AND project != 'observer-sessions'
ORDER BY project ASC
`);
+9
View File
@@ -105,6 +105,9 @@ export class PaginationHelper {
// surfaces observations that originated under its merged children.
conditions.push('(o.project = ? OR o.merged_into_project = ?)');
params.push(project, project);
} else {
// Hide internal observer-session rows from the unfiltered UI list.
conditions.push("o.project != 'observer-sessions'");
}
if (platformSource) {
conditions.push(`COALESCE(s.platform_source, 'claude') = ?`);
@@ -163,6 +166,9 @@ export class PaginationHelper {
// surfaces rows that originated under its merged children.
conditions.push('(ss.project = ? OR ss.merged_into_project = ?)');
params.push(project, project);
} else {
// Hide internal observer-session rows from the unfiltered UI list.
conditions.push("ss.project != 'observer-sessions'");
}
if (platformSource) {
@@ -214,6 +220,9 @@ export class PaginationHelper {
if (project) {
conditions.push('s.project = ?');
params.push(project);
} else {
// Hide internal observer-session rows from the unfiltered UI list.
conditions.push("s.project != 'observer-sessions'");
}
if (platformSource) {
+4
View File
@@ -75,6 +75,10 @@ export const VECTOR_DB_DIR = join(DATA_DIR, 'vector-db');
// Sessions here won't appear in user's `claude --resume` for their actual projects
export const OBSERVER_SESSIONS_DIR = join(DATA_DIR, 'observer-sessions');
// Project name assigned to observer sessions (basename of OBSERVER_SESSIONS_DIR).
// UI queries filter this out so internal worker sessions don't pollute project lists.
export const OBSERVER_SESSIONS_PROJECT = 'observer-sessions';
// Claude integration paths
export const CLAUDE_SETTINGS_PATH = join(CLAUDE_CONFIG_DIR, 'settings.json');
export const CLAUDE_COMMANDS_DIR = join(CLAUDE_CONFIG_DIR, 'commands');