Part of the DEVIN ecosystem
Track: Understand · Nature: infrastructure
For: Developers, Maintainers
Interface: CLI / Python library
Path: Developers · step 3/4 — afterdevin-skill-catalog, beforedevin-powerups
devin-internals-spec
Linux · Personal Windows · Corporate Windows
Part of the awesome-devin ecosystem: the curated hub for the devin-* tools.
Unofficial community project. Not affiliated with, endorsed by, or sponsored by Cognition AI. "Devin" is a trademark of Cognition AI.
A documented map of Devin's local session stores — the CLI sessions.db, the
Desktop GUI acp-messages/*.db and state.vscdb — plus a schema-version
detector and deterministic synthetic fixtures, so tooling fails loudly instead
of silently misreading your data.
The problem
Everything Devin stores on your disk is kept in undocumented SQLite databases
whose schema changes without notice: sessions.db has already gone through
17 migrations (v1–v15 on 2026-05-06, v16 on 2026-07-05, v17 on 2026-09-14).
Any tool that reads these stores has to reverse-engineer the format — and when
Cognition ships the next migration, those tools break silently: wrong counts,
missed rows, or corrupted interpretation, with no error raised.
The Devin Desktop app is worse off than the CLI: its stores
(acp-messages/*.db, state.vscdb, session locks, the
refinery_schema_history migration ledger) are not documented anywhere.
Prior art
Session-log tooling exists for other agent CLIs — e.g. tokmesh and
UniSessions document and parse Devin's CLI sessions.db. This project does
not reinvent that: it borrows the same idea (SQLite schema → typed parsing) and
extends it to what those tools don't cover — the Desktop GUI stores — and adds
the piece they lack: an explicit schema-version gate so a parser refuses to
run against a schema it has never seen.
What makes it Devin-native
The differentiator, in one sentence: it's the map of what Devin keeps on your disk — and it warns you when that changes.
- Side-by-side: it covers
acp-messages/*.db,state.vscdb(windsurfSpace.*keys) andrefinery_schema_history, which no existing tool documents. - No-Devin: remove Devin and there is no store to document — the extra disappears entirely.
- Loud failure:
detect_schema_version()returns a version contract and raises on anything unknown, instead of guessing at bytes.
Install
Python ≥ 3.10 required; install with uv (recommended) or pipx.
uv tool install devin-internals-spec
or with pipx (install it via pip install --user pipx or your package manager, then pipx ensurepath):
pipx install devin-internals-spec
For development:
pip install -e ".[dev]"
pytest
Usage
devin-inspect schema <sessions.db> # schema-detection contract (JSON)
devin-inspect sessions <sessions.db> # list sessions (id, title, cwd, …)
devin-inspect health <devin-data-dir> # locate + check all three stores
devin-inspect contract <devin-data-dir> # unified drift check: schema +
# acp meta + usage shape, one report
devin-inspect make-fixture <out> # deterministic synthetic stores
# for tests/demos (--kind, --seed,
# --schema-version, --n-sessions)
contract is the single drift boundary for the whole catalog: it reports
sessions.db schema version, acp-messages meta schema_version (observed:
6; 1 on legacy DBs) and unexpected meta keys, and the verified usage shape
(no cost persisted — num_tokens_preceding only). On Linux it resolves the
split layout automatically (~/.local/share/devin data root +
~/.config/Devin GUI root). Exit code is non-zero on any drift.
AcpMessagesStore.typed_meta() decodes the meta table into a typed
view (schema_version normalized to int, message_count, truncated,
title from the info JSON blob) with raw as fallback.
devin_internals.projects gives the canonical project key/path used to
join sessions across tools, and devin_internals.commits.commit_references()
extracts git commit SHAs (40-hex + /commit/<sha> URLs, with repo context)
from tool_call_state payloads — short SHAs are ignored on purpose
(precision over recall for graph joins).
Every subcommand is read-only and accepts --json. The Python library is the
supported interface — the CLI is a thin wrapper:
import os
import sys
from pathlib import Path
from devin_internals import detect_schema_version
from devin_internals.parsers import SessionsStore
if os.name == "nt":
root = Path(os.environ["APPDATA"]) / "devin"
elif sys.platform == "darwin":
root = Path.home() / "Library" / "Application Support" / "devin"
else:
data_home = Path(os.environ.get("XDG_DATA_HOME", Path.home() / ".local/share"))
root = data_home / "devin"
sessions_db = root / "cli" / "sessions.db"
detect_schema_version(sessions_db)
# {"schema_version": 17, "known": True, "min_supported": 15, ...}
with SessionsStore(sessions_db) as store:
store.sessions() # typed dataclasses
Canonical store locations (paths + identity)
devin_internals.paths is the canonical convention for where Devin keeps
its on-disk state — the reference the ecosystem's vendored paths.py copies
(11+ packages) converge on (decision D-2026-10-08):
| platform | data root | UI config root |
|---|---|---|
| Linux | $XDG_DATA_HOME/devin → ~/.local/share/devin |
$XDG_CONFIG_HOME/Devin → ~/.config/Devin |
| Windows | %APPDATA%\devin |
%APPDATA%\Devin |
| macOS | ~/Library/Application Support/devin |
~/Library/Application Support/Devin |
resolve() returns a frozen DevinPaths with every store a consumer needs:
sessions_db, session_locks_dir and credentials_toml under the data root;
acp_messages_dir and state_vscdb under the config root's User/ tree; and
ecosystem_dir — <config>/.devin-ecosystem/ — anchoring ecosystem
bookkeeping (machine-id, devin-profile.json, hooks.json,
scheduled.json, plans.jsonl, metrics/). DEVIN_DATA_DIR /
DEVIN_CONFIG_DIR override the roots; candidate-order helpers cover files
that may live under data or config roots — including the legacy
lowercase $XDG_CONFIG_HOME/devin CLI root on Linux — and a unified
User/-under-data layout is detected per store. The ecosystem anchor is
resolved separately from UI-store probing (ecosystem_anchor_dir), so a
User/ tree appearing later can never move an existing identity.
environ/platform are injectable for tests. Per-OS path details live
in the platform guides: Linux,
Windows, Corporate
Windows.
devin_internals.identity resolves the shared machine identity the same way
every devin-* tool does: machine_id() returns DEVIN_MACHINE_ID or a random
16-hex id published once at <ecosystem>/machine-id (mode 0600, atomic
link-claim — on filesystems with hard-link support the file never exists
empty or partially written; concurrent losers adopt the winner, never
regenerated); profile()
resolves DEVIN_ECOSYSTEM_PROFILE, then devin-profile.json (canonical
ecosystem location first, then the legacy config-root copies, lowercase
sibling included), failing closed
to "corporate" whenever a resolved value is invalid.
from devin_internals.paths import resolve
from devin_internals.identity import provenance
paths = resolve() # frozen DevinPaths for this machine —
# the one API that picks platform conventions;
# pass environ=/platform= to resolve for another
paths.sessions_db # <data>/cli/sessions.db
paths.ecosystem_dir # <config>/.devin-ecosystem
provenance() # {"machine_id": ..., "profile": "corporate"|"personal"}
Incremental cursors (devin_internals.cursor)
Cursor is the shared incremental-watermark helper — one JSON object
per cursor at <dir>/<name>-state.json, for learning extract, sweeps,
digests and any "resume where I stopped" mechanism (spec:
devin-powerups/docs/incrementality.md).
- The anchor dir defaults to
<cwd>/.devinand is overridable per cursor via theDEVIN_CURSOR_DIRenv or an explicitdir=argument. commit()is the only write: atomic (tmp +os.replace), called after the work so a crash re-scans instead of skipping, and it merges with whatever is on disk so two cursors sharing a name do not erase each other's keys.- Missing or corrupt state reads as empty (fail-open); non-finite
values (
NaN,inf) are rejected at commit time.
from devin_internals import cursor
c = cursor.Cursor("learning-extract")
last = c.get("last_ts", 0)
# ...process everything newer than last...
c.set("last_ts", newest_ts)
c.commit()
Works with Devin alone (Devin-only mode)
The spec is documentation plus a small local validation library: it reads Devin's stores to report the schema version and gates tools when the version is newer than what is known — it stops loudly instead of misparsing. Purely local; nothing external is needed.
Platform support
Pure stdlib Python — identical behavior on Windows, Linux and macOS. CI runs
the suite on windows-latest + ubuntu-latest. Parsers and CLI
subcommands take the target file or directory as an explicit argument;
only devin_internals.paths/identity resolve platform conventions,
and both accept injectable environ/platform plus the
DEVIN_DATA_DIR/DEVIN_CONFIG_DIR overrides.
vscdb-scan — shape-only audit (IS-1)
devin-internals vscdb-scan <state.vscdb> audits the GUI key/value store
and reports, per key: name, value shape (json-object/array/string/…),
length, top-level JSON keys and risk flags (sensitive-key-name,
looks-like-jwt, large-blob). Values are never printed — flags are
heuristic and recall-biased; a real audit confirmed state.vscdb can
hold auth tokens and PII. --fail-on-flags exits 1 for CI.
Limitations
- Private, volatile internals. These stores are implementation details of
Devin; the schema can change in any release. Only what is marked "verified"
in
docs/SPEC.mdshould be relied on. - Verified against schema v17 only (the latest at time of writing). The detector refuses versions it doesn't know — that's a feature, but it means new Devin releases will break detection until the spec is updated.
- Read-only. This project never writes to Devin's databases.
- Not a session exporter. It documents and guards the format; it does not
dump your conversations (that is
devin-history's job). - Fixtures contain synthetic data only — no real session content is ever copied, by design.
Development
pip install -e ".[dev]"
pytest
Ground rules in CONTRIBUTING.md: fixtures before parsers, small commits, a shared README and Windows/Linux platform guides.
When to use this
- You are building a tool that reads Devin's local stores and need the documented schema, not reverse-engineering.
- You want a schema-version gate:
detect_schema_version()refuses loudly on unknown versions instead of misparsing. - You need deterministic synthetic fixtures to test parsers — no real session data required.
- You need typed, read-only access to
sessions.dbviaSessionsStorein Python.
When NOT to use this
- You want to export or dump sessions — this documents and guards the format;
devin-historydoes the exporting. - Your Devin release ships a schema newer than v17 — the detector refuses by design until the spec catches up.
- You need writes — every parser and subcommand is strictly read-only.
FAQ
What data does Devin store locally, and where? Per this spec: a versioned sessions.db under cli/ (sessions, tool-call state, transcripts), one acp-messages/*.db per GUI session, a state.vscdb KV store with windsurfSpace.* keys, credentials.toml, and a refinery_schema_history migration ledger. The verified details live in docs/SPEC.md.
What happens when Devin ships a new schema version? Tools using this library fail loudly, not silently. detect_schema_version() returns a contract (known, min_supported) and raises on versions it does not recognize — verified against schema v17 at time of writing.
How is this different from other sessions.db parsers? It also documents the Desktop GUI stores (acp-messages/*.db, state.vscdb, session locks, the migration ledger) that other tools don't cover, and adds the explicit schema-version gate. The library is the supported interface; devin-inspect is a thin CLI wrapper.
License
MIT — see LICENSE.
If this saved you debugging time, a ⭐ on the repo helps others find it.
Metadata
Release files for devin-internals-spec 0.3.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| devin_internals_spec-0.3.2.tar.gz | 55.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| devin_internals_spec-0.3.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 98.6 kB
Release files / devin_internals_spec-0.3.2.tar.gz
| Download URL | devin_internals_spec-0.3.2.tar.gz |
|---|---|
| Size | 55.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dc38baa7d35da5b55dc3b17170bafff27c6e3bd2278c3bdde62384d64b31ae71
|
|
BLAKE2b-256 checksum How to use checksums |
b41566e1be503979305fd451c48ea85123274261da7740f30f3936970b5b49c7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.
Transparency logRelease files / devin_internals_spec-0.3.2-py3-none-any.whl
| Download URL | devin_internals_spec-0.3.2-py3-none-any.whl |
|---|---|
| Size | 43.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
47fbe2fb997e7d302ad85a0853ccdf3206bfaf3f49a11b231fb3ca5ebc7a0586
|
|
BLAKE2b-256 checksum How to use checksums |
4491d2eeae90f59cbee0641f2ce2ddaa058b50265f9daccbf63aae4b7a5b3a75
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.
Transparency log