Capture chats

Capture is what gets a conversation off the vendor's servers and onto your filesystem. It happens in the browser via the Chrome extension; the desktop app is the receiver.

Supported platforms

The extension knows how to read chats from these five today:

  • ChatGPT (chatgpt.com)
  • Claude (claude.ai)
  • Gemini (gemini.google.com)
  • Grok (grok.com)
  • Kimi (kimi.com)

Each platform has its own provider script under extension/platforms/. Each script knows how to walk that platform's conversation list, fetch a single conversation in full, and download referenced images. See How it works for the per-platform details.

Two ways to capture

@kept palette (manual control)

Type @kept in any supported chat's input box. A small palette appears inline over the trigger text with options:

  • Save current conversation — the whole thread
  • Save last N messages — a slice
  • Save this branch — for branched conversations (ChatGPT, Claude)

Pick one and the extension fetches the conversation, hands it to the desktop app, and writes a markdown file under ~/.kept/vault/<platform>/.

The trigger text is removed from your input box on selection — it doesn't get sent to the chat platform.

Auto-sync (background capture)

Open the extension popup → enable Auto-sync. The extension polls the platforms you've toggled on, on a configurable interval (default: every 10 minutes), and writes any new conversations to the vault automatically.

Auto-sync uses SHA-256 deduplication: if a conversation hasn't changed since the last sync, it isn't rewritten. If it grew (new messages), only the diff gets appended to the existing file.

Where files land

~/.kept/vault/
├── chatgpt/
├── claude/
├── gemini/
├── grok/
└── kimi/

One directory per platform, one markdown file per conversation. Filenames are YYYY-MM-DD-<slug>.md.

You can move the vault by setting KEPT_VAULT_DIR in your shell environment, or via Settings → Vault → Vault directory in the desktop app.

File format

Each file is plain markdown with YAML frontmatter:

---
platform: claude
title: RLS rollout
date: 2026-04-24
messages: 23
tags: [postgres, security]
source_id: 8f3a-claude-conversation-id
---

# RLS rollout

> User: We have to roll out row-level security across the tenant tables...

The frontmatter is what's queryable. The body is just markdown — code blocks, math, tool calls (rendered as YAML), images (downloaded to assets/ and referenced by relative path).

What the extension does and doesn't touch

  • Reads what you're already authorized to read. The extension uses your existing session — it doesn't store credentials, doesn't proxy through a third party, doesn't have its own server-side state.
  • Writes only to the local Kept app via localhost:18241. Never out to the internet.
  • Doesn't post anything to the chat platform. @kept is intercepted in the input box before submission.

For provider-by-provider details on what API endpoints get called and how images are handled, see How it works.

Permissions

The extension's manifest.json requests host permissions for the five chat domains plus a couple of CDN domains (files.oaiusercontent.com, assets.grok.com, etc.) needed to download referenced images. It also requests storage, alarms, and idle for auto-sync scheduling.

It does not request <all_urls> for general data access. The one all-URLs content script is command-palette.js, which is the inline @kept trigger handler — it only attaches a key listener and renders an in-DOM palette element.