Skip to content

Add private session mode to exclude sessions from memory ingestion #247

Description

@geier

Context

The memory ingestion watcher (#241) automatically captures every OpenCode session into the Graphiti knowledge graph. However, some sessions contain sensitive content (credentials, private discussions, experimental code) that should NOT be recorded in the shared memory system.

Users need a way to mark a session as "private" so the watcher skips it during ingestion.

Constraint

The OpenCode Session API has a fixed schema with no extensible metadata field. session.update() only accepts title and time.archived. There is no tags, metadata, or flags field available. This means we cannot store a "private" flag on the session object itself — we need a separate mechanism.

Goal

  1. Add a UI toggle to mark sessions as private (excluded from memory ingestion)
  2. The watcher respects the private list and skips those sessions
  3. Private sessions should be visually distinct in the UI

Design

Storage: Watcher exclusion list

The watcher already maintains a state file for deduplication (~/.local/share/opencode-memory/state.json, from #241). Extend this to include a private list:

{
  "ingested": {
    "ses_abc123": { "lastMessageTime": 1713000000 }
  },
  "private": ["ses_abc123", "ses_def456"]
}

The watcher checks this list before ingesting any session. If the session ID is in private, it is skipped entirely.

Sync mechanism: Shared state file or watcher API

Two options (implementer should choose the simpler one):

Option A — Shared file: The UI server writes to the same state file the watcher reads. Both processes are on the same machine. The UI server gets a new extended API endpoint:

POST /api/ext/memory/private   { sessionID: string, private: boolean }
GET  /api/ext/memory/private   → { sessions: string[] }

This endpoint reads/writes the watcher's state file directly.

Option B — Watcher HTTP API: The watcher exposes a small HTTP API (e.g., port 8766) for managing the private list. The UI server proxies to it.

Option A is simpler and recommended since both processes run on the same machine.

UI: Session header toggle + slash command

Session header: Add a lock/shield icon button next to the existing session action buttons (share, archive, etc.) in the session header. When toggled:

  • Icon changes to indicate private state (e.g., filled lock)
  • A subtle visual indicator appears (e.g., badge or border color)

Slash command: /private toggles private mode for the current session.

Sidebar indicator: Private sessions show a small lock icon in the session list.

New session flow: Consider adding a "Start private session" option alongside the existing "New Session" button, or a keyboard shortcut.

Visual Design

  • Private sessions should have a subtle but clear visual indicator
  • Suggested: small lock icon in the session sidebar entry
  • Suggested: "Private" badge or lock icon in the session header
  • Should NOT be distracting — most sessions will be non-private

Files to create/modify

UI (app-prefixable/)

  • app-prefixable/src/context/memory.tsx — new context provider for memory/private state (fetches private list, provides toggle function)
  • app-prefixable/src/pages/session.tsx — add /private slash command, add header toggle button
  • app-prefixable/src/components/session-header.tsx — add private indicator
  • app-prefixable/src/components/session-sidebar.tsx — add lock icon for private sessions
  • app-prefixable/src/pages/layout.tsx — add "Start private session" option (optional)

Server (shared/)

  • shared/extended-api.ts — add POST /api/ext/memory/private and GET /api/ext/memory/private endpoints

Watcher (memory/)

  • memory/watcher/state.ts — extend state model with private list
  • memory/watcher/index.ts — check private list before ingestion

Reference

  • Session header actions: app-prefixable/src/components/session-header.tsx
  • Session sidebar: app-prefixable/src/components/session-sidebar.tsx and app-prefixable/src/pages/layout.tsx (lines 2350-2560)
  • Extended API pattern: shared/extended-api.ts (see existing DELETE /api/ext/mcp/:name for the pattern)
  • Slash command registration: app-prefixable/src/pages/session.tsx (search for registerCommand)
  • Watcher state file: memory/watcher/state.ts (from Build background session ingestion watcher #241)
  • Session type has no metadata field: app-prefixable/src/sdk/gen/types.gen.ts line 807

Future Improvement

If/when the upstream OpenCode API adds a metadata or tags field to the Session type, the private flag should migrate to live on the session object itself. This would eliminate the need for the separate state file and make the feature work without the watcher being installed. File an upstream feature request at https://github.com/anomalyco/opencode if this epic proves the concept.

Acceptance Criteria

  • Users can toggle a session as private via UI button or /private slash command
  • Private sessions show a visual indicator in the sidebar and session header
  • The watcher skips private sessions during ingestion
  • Toggling private on a session that was already ingested does NOT retroactively remove it from the knowledge graph (document this limitation)
  • Private state persists across browser refreshes and watcher restarts
  • "Start private session" option available at session creation (optional, nice-to-have)

Metadata

Metadata

Assignees

Labels

needs-supervisorBlocked, needs guidancepriority:highImportant feature or significant bugtaskIndividual task issue

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions