v0.7.0
This release lands first-class support for the Cursor provider end-to-end — sync intake, analytics, transcript rendering, and docs — alongside share-hygiene hardening, an admin unpriced-models surface, and several Trends/transcript fixes.
Highlights
- Cursor provider support — Confab now ingests, analyzes, and renders Cursor agent transcripts (main + subagent threads), with provider registration, sync intake, analytics aggregation, and a dedicated transcript pane.
Features
Cursor provider (new)
- #349 Cursor wire-format spec + sanitized JSONL test fixture
- #350 Cursor backend — provider registration, sync intake, analytics
- #352 Re-land Cursor frontend onto main (stranded by stacked #351)
- #353 User-facing Cursor provider docs (Starlight)
- #354 Add Cursor to hardcoded marketing/landing provider copy
- #355 Clamp future-dated session timestamps across all sync intake paths
- #356 Actionable Cursor Summary analytics errors + regression coverage
- #357 Cursor session time bounds + estimated
DurationMs - #358 Strip Cursor-native
[REDACTED]from assistant text during normalize - #360 Extract
<user_query>from Cursor user messages (hide envelope tags) - #361 Collapsible injected-context sections in Cursor user rows
- #362 Markdown rendering for Cursor user + assistant rows
- #363 Estimated per-row timestamps in the Cursor transcript pane
- #365 Cursor session card
models_usedmust be[]notnull - #366 Document Cursor-native
[REDACTED]wire semantics (not Confab redaction) - #367 Explain unavailable Cursor token/cost metrics in Summary and Trends
- #368 Cursor transcript per-row action parity (copy payload + deep-link + skip-nav)
- #369 Re-enable the TimelineBar minimap in the Cursor transcript pane
- #370 Persist Cursor sync-metadata model into analytics
models_used - #372 Cursor subagent transcripts — analytics + search aggregation (re-land)
- #373 Subtle Cursor unavailable-tokens note below the smart recap
- #374 Explain missing Cursor Conversation turn timing via a card footer
Other features
- #333 Model new Bash
toolUseResultfields (persisted-output,returnCodeInterpretation,interrupted/isImage/noOutputExpected) - #342 Share-creation daily quota + upfront email batch rate-limit check
- #346 Surface Claude Code auto permission mode in transcript parsers
- #347 Periodic physical cleanup of long-expired session shares
- #348 Admin surface for unpriced models (pricing-gap visibility)
Bug Fixes
- #332 Subtle always-visible Y-axis on Trends Cost Distribution card
- #344 Scroll BashOutput body when output exceeds
maxHeight - #345 Widen Cost Distribution Y-axis so $ cost labels aren't clipped
Security / Hardening
- #338 Serialize admin bootstrap with a Postgres advisory lock
- #340 Reject deactivated users at OAuth login (break the login loop)
- #343 Filter expired shares from list endpoints + share-hygiene comments
Refactoring
- #334 Retire flat
session_card_tokens(v1) — migrate Trends daily + precompute gate, stop writing v1 - #336 Gate filter options on session listability (re-land onto main)
- #337 Keep API key limit in sync — embed limit in backend 409, drop frontend
MAX_API_KEYS - #339 Extract
validateOAuthCallback+checkUserEligibilityOAuth helpers
DB Migrations
- 000055 —
cursor_session_meta: New sidecar table persisting the per-session model name the Cursor CLI sends asmetadata.model(Cursor JSONL transcripts carry no model field). Columns:session_id(UUID PK, FK tosessionswithON DELETE CASCADE),model(VARCHAR(255)NOT NULL),created_at,updated_at. Mirrors thecodex_rolloutsread-back precedent so the session card'smodels_usedcan be populated without adding a column to the genericsessionstable.
API Changes
- Sync —
providerfield: now accepts"opencode"and"cursor"in addition to"claude-code"and"codex". Omitting still defaults to"claude-code". - Sync — new chunk-metadata fields (for providers whose transcript lines carry no per-line signals, i.e. Cursor):
metadata.latest_message_at(RFC3339) — explicit latest-message timestamp; advancessession.last_message_at. Values >48h in the future are silently dropped.metadata.created_at(RFC3339) — explicit session creation time; start anchor for estimating duration. Lowersfirst_seenwhen earlier (never raises it). Values >48h in the future are silently dropped.metadata.model(string, ≤255) — model that produced a Cursor session; persisted in thecursor_session_metasidecar (first non-empty wins) and surfaced ascards.session.models_used.metadata.first_user_message— for Cursor sessions, the value is unwrapped from its<user_query>…</user_query>envelope before storage so the session-list title shows the human prompt.
- New endpoint —
GET /api/v1/admin/unpriced-models(super-admin only): returns model families seen in stored session data that are absent from the active pricing table, grouped by provider, ordered by session count. GET /api/v1/admin/system-shares: expired shares (expires_at <= NOW()) are now omitted from the listing.cards.session.models_used: now always a JSON array, nevernull.- Trends
filter_options(owners/repos/models): now gated on session listability — each option maps to ≥1 listable session, so options never orphan to an empty list. - 409 Conflict (API key limit): error message now includes the actual maximum (e.g. "maximum of 500 API keys").