Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Windsurf

← all harnesses · parser: crates/core/src/session/windsurf.rs

The least legible of the eight, and the only page here with a standing caveat on its own accuracy. Windsurf is a VS Code fork, so its conversations live inside the editor’s per-workspace settings database, and its accounting lives on Codeium’s servers where nothing local can see it.

Where it lives

$WINDSURF_USER_DIR/workspaceStorage/<hash>/state.vscdb. Without the override, the User directory follows the platform: ~/Library/Application Support/Windsurf/User on macOS, %APPDATA%\Windsurf\User on Windows, and the XDG config directory elsewhere. $WINDSURF_USER_DIR names the whole User directory rather than a parent, which is what a portable install actually moves.

The working directory comes from workspace.json beside the database, where VS Code records it as a file:// URI; it is percent-decoded by hand, because undoing %XX is the entire job and a URI crate would be a dependency for six lines. A remote or virtual workspace has no local path and keeps an empty label.

The format

SQLite, opened read-only. ItemTable maps a settings key to a JSON blob, and Cascade parks its conversations in one of those blobs as a list of tabs, each holding the bubbles of one conversation. One row per tab: tabId for the id (string or number, both accepted), chatTitle for the title.

The stored value is taken as raw bytes, Text or Blob alike: VS Code writes some ItemTable entries as one and some as the other, and asking rusqlite for a concrete type fails outright on the wrong one.

The caveat

ponytail: the parser is written against the documented ItemTable layout rather than a Windsurf install, so the settings-key list and the bubble field names are the part that could be wrong.

Four keys are tried in order — cascade.chatdata, workbench.panel.aichat.view.aichat.chatdata, aiChat.chatdata, chat.data — and the first that parses into tabs wins. Tool calls are read from either toolCalls or tool_calls, with a name under either name or toolName.

Both fail closed: an unrecognised key yields no rows, an unrecognised bubble yields no tool calls. So a wrong guess costs visibility and never produces a wrong number. If you can read the real key off a live install, add it to CHAT_DATA_KEYS. Until then, the README’s ✓ in Windsurf’s Tools column is provisional in a way the other seven are not.

What cannot be extracted

No model, no tokens, no cost, no per-message timestamp, no tool arguments, no per-call outcome. Windsurf bills in credits server-side and keeps none of it locally, so list_sessions sets cost_available = false and total_cost = None: these sessions report cost as unavailable rather than as free. Tool details carry the tool’s name and nothing else, because a fabricated argument line would read as though the blob had recorded one.

Both timestamps are the database’s mtime. Windsurf stamps neither a start nor an end on a conversation, so every tab in a workspace shares the file’s time. That is honest about ordering — the most recently used workspace really did change last — and deliberately says nothing else.

The consequence reaches the cache: cache::disk_key returns None for Windsurf, because there is no per-conversation stamp to build an honest key from, so its rows are never persisted and re-extract together whenever the workspace file moves. That is cheap, since the blob is small.

What cctop will not do

delete returns Unsupported with a message pointing at the editor. Removing one conversation means rewriting a settings blob inside a live editor’s own database, which is not a thing to do behind a running Windsurf — and reporting the refusal beats returning success and leaving the row on screen.

There is no resume command, no process to match (Windsurf’s agent is the editor itself), and no hooks: access.rs lists a project’s .windsurfrules and notes that the global rules live in the settings UI, where nothing can read them.