| name | session_persistence |
| description | Protocol for local session metadata persistence in the Virgo extension. |
Session Persistence Protocol
1. Rationale
To ensure human-readable session titles and turn metadata are correctly resolved in the UI without relying on central agent memory, all session-specific state must be persisted locally.
2. Storage Architecture
- Antigravity Root:
~/.gemini/antigravity/ (absolute, OS-normalized)
- Snippet Root:
~/.gemini/antigravity/virgo/
- Session Folder:
~/.gemini/antigravity/virgo/<sessionId>/
- State File:
~/.gemini/antigravity/virgo/<sessionId>/extension_state.json
- Snippet Files:
~/.gemini/antigravity/virgo/<sessionId>/<timestamp>_<name>.md
[!IMPORTANT]
The _antigravityRoot field in SpeechProvider is set to path.join(root, 'virgo') — NOT the bare antigravity/ root.
_getSnippetHistory() scans _antigravityRoot directly. Session directories are immediate children of this path.
3. Metadata Schema (extension_state.json)
{
"session_title": "Human Readable Title",
"current_turn_index": 0,
"last_updated": "2026-04-05T00:00:00Z"
}
4. Resolution Protocol
4.1 UI Resolution (Snippet History)
In speechProvider.ts, use _getSnippetHistory() to resolve the displayName:
- Check for
extension_state.json in the session directory.
- If present, parse
session_title.
- If absent or empty, fallback to the
sessionId (truncated).
4.2 MCP Injection
In mcpStandalone.ts or mcpBridge.ts, the say_this_loud tool must accept an optional session_title:
- Update
extension_state.json with the new title if provided.
- Increment
current_turn_index atomically.
- Guard: Adhere strictly to the verbatim parity rules defined in virgo_injection_guard.
5. Maintenance
- Update Location: Metadata updates MUST target
extension_state.json.
- Abolition:
state.json in the brain/ directory is deprecated and MUST NOT be used for session vitals.
6. Snippet Scanner Exclusion List
[!NOTE]
Observed: 2026-04-10. _getSnippetHistory() iterates ALL directories in antigravityRoot (virgo/).
System-only directories are NOT user sessions and produce empty results but waste I/O and clutter
session discovery. They MUST be excluded from the scan.
Directories to Filter (in _getSnippetHistory())
| Directory | Reason |
|---|
brain/ | Agent memory (already excluded — line 802) |
protocols/ | Agent protocol templates — not a user session |
tempmediaStorage/ | Transient media files — not a user session |
Implementation
const EXCLUDED_DIRS = new Set(['brain', 'protocols', 'tempmediaStorage']);
if (EXCLUDED_DIRS.has(name)) { return null; }
Files in Root (Non-Session)
These are filtered automatically by the type !== FileType.Directory check:
active_servers.json
mcp_discovery.json
- Any future flat files added to the root
7. Dual-Path Architecture — Agent Artifacts vs. Snippets
[!IMPORTANT]
There are TWO distinct storage paths used by the system. Conflating them will cause the Snippet Discovery sidebar to show empty.
| Path | Purpose | Scanner Reads It? |
|---|
brain/<sessionId>/ | Agent artifacts (task.md, implementation_plan.md, media, scratch) | ❌ NOT scanned by _getSnippetHistory() |
virgo/<sessionId>/ | User-playable snippet .md files injected via MCP say_this_loud | ✅ YES — this is where _getSnippetHistory() reads from |
Implication for MCP Injection
When say_this_loud is called by the AI agent (via mcp_virgo_say_this_loud):
- The
.md file MUST be written to virgo/<sessionId>/<timestamp>_<snippet_name>.md
- If instead it is written to
brain/<sessionId>/, it will NEVER appear in the Snippet Discovery UI
Symptom: Empty Snippet Discovery
If the Snippet tab shows "No injected snippets found" for the current session, the most likely cause is:
- The MCP injection tool wrote the file to
brain/<sessionId>/ instead of virgo/<sessionId>/
- The session directory exists in
virgo/ but contains only extension_state.json (no .md files)
Remedy: Verify that say_this_loud writes to the correct virgo/ path before troubleshooting the scanner.