# Ten agent session roots, one SQLite index, and three providers that admit when history was retracted

> Obelisk indexes the local session history of nine coding agents into one SQLite file that an agent skill can query and an Electron app can browse. The interesting part is the honesty in its own documentation: a table showing which providers can attest that history was retracted, and three named cases where the tool declines to guess at a session directory.

**tommy0103/obelisk** — Every past session, subagent, and workflow -- queryable by your agent, browsable by you

- Repository: https://github.com/tommy0103/obelisk
- Website: https://obelisk.antinomie.org
- Stars: 550 · Forks: 40
- Language: JavaScript
- License: AGPL-3.0
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/tommy0103-obelisk

## The manifest says 0.2.0 while the newest tag is v0.2.4

The package manifest and the release history disagree about which version this is. `package.json` carries:

```json
  "name": "obelisk",
  "version": "0.2.0",
  "private": true,
  "type": "module",
```

The newest tag is v0.2.4, published on 2026-10-02. Between the manifest value and the tag there are four patch releases, and v0.2.3 does not appear among the three most recent releases at all, so the sequence runs v0.2.1, v0.2.2, v0.2.4. The `private` flag explains why the root is not published: this is a workspace root with `packages/*` members, not a package anyone installs from a registry, and the version field is bookkeeping rather than a release coordinate.

The release names are uneven in the same way. v0.2.2 is titled with what it added, inline images, source links and a cleaner session history. v0.2.1 is titled with what it added, Kimi Code joining the index. v0.2.4 is titled `Obelisk v0.2.4` and carries no scope at all. So for the newest release the tag tells you a version happened and nothing about what changed, which is the one release where you would most want the detail.

The rest of the shape: AGPL-3.0 licensed, 550 stars, 40 forks, 55 open issues, default branch `main`, last push dated 2026-10-01, and a homepage on a personal domain rather than GitHub Pages. Fifty five open issues against forty forks is a high open share, and for a tool whose input is other people's session logs, the queue is probably where interoperability reports land.

## Nine provider roots, one database, and one of them opened read only

Both halves of the product read the same file, `~/.obelisk/obelisk.sqlite`. The CLI owns the local runtime and an agent skill teaches agents to query it; the Electron app is for humans to browse sessions, manage memories, check usage and see weekly recap cards. The indexer is where the aggregation happens, and it walks a specific root per provider:

Claude Code transcripts from `~/.claude/projects`, Codex from `~/.codex/sessions` and `~/.codex/archived_sessions`, GitHub Copilot from VS Code Stable and Insiders user data roots, DeepSeek Harness from `~/.dsh/sessions` or `$DSH_HOME/sessions`, Hermes Agent from `~/.hermes/state.db` or `$HERMES_HOME/state.db`, Kimi Code from `~/.kimi-code/sessions` or `$KIMI_CODE_HOME/sessions`, OMP from `~/.omp/agent/sessions`, Pi from `~/.pi/agent/sessions`, and ZCode from `~/.zcode/cli/db/db.sqlite`.

Four of those roots are another application's SQLite database rather than a directory of transcripts. ZCode's is opened read only and its write ahead log sidecar is watched alongside it, and Hermes is a `state.db` read under a profile layout. So for those two providers the indexer attaches to a live database someone else owns.

One detail worth separating from the rest: Codex also writes a `session_index.jsonl`, and that file is used as lightweight title and update metadata during indexing rather than as the message transcript source. Getting that wrong would mean indexing an index, and the documentation is explicit that it does not. For live refresh in the app, Obelisk watches every root declared by every registered provider, which is a file watcher per provider rather than one polling loop.

## Only three of the nine providers can attest that history was retracted

This is the part of the design that decides whether an answer is trustworthy, and it is laid out as a table rather than a promise. Each provider gets a row on superseded history support:

| Provider | Superseded-history support |
| --- | --- |
| Pi | Branch, leaf, and compaction state attests inactive history |
| OMP | Branch, leaf, and compaction state attests inactive history |
| ZCode | Rewind retention and compaction attest inactive history |
| Kimi Code | Undo/clear can attest supersession; preservation is a follow-up |
| Hermes Agent | Superseded history is not split: compaction-archived and rewound rows are both stored as `inactive` |
| Claude Code | The source does not attest rewind or current-leaf state |
| Codex | Sessions have no branching semantics |

Read the last two rows first. The source does not attest rewind or current leaf state means the agent's own logs contain nothing that distinguishes a message the user later undid from one still standing, so no index can mark the first as inactive. Codex has no branching semantics at all, which is a cleaner case: if nothing is ever superseded, nothing needs marking.

Hermes is the failure mode in the other direction. It does have retraction, and the index records it, but it stores both compaction archived rows and rewound rows as `inactive` without splitting them. A query cannot then distinguish a branch the agent compacted away from a branch the user rewound, which are different events with different reasons.

Kimi is the honest middle. Undo and clear can attest supersession, so retracted records leave the index; preservation, meaning keeping them retrievable under an explicit flag, is named as a follow-up rather than shipped. Pi is the reference behaviour, with `includeInactive: true` on supported query helpers, and superseded entries stored as `inactive` while display suppressed or transport only records are stored as `hidden` and never returned by those helpers at all. The distinction between those two states is the one that keeps a rewritten record from reappearing in an agent's answer.

## Session identity is a hash of the path, which is what stops two projects colliding

Putting nine providers in one schema means one identity scheme, and this one is the part most likely to surprise you. Every non Claude id is provider prefixed so two providers cannot collide. Within a provider, the trouble starts with Pi and OMP, whose explicit session ids are project local: two projects can hand out the same custom id, and the same project moves on disk. Obelisk combines each provider's header id with a deterministic hash of the normalized header `cwd`, which keeps identities stable across file moves while letting two projects use the same custom id.

Hermes gets a different treatment. Its sessions are scoped by the store they were read from as well as the profile name, so a copied or migrated `state.db` cannot collide with the original. That is a direct answer to the failure mode of anyone who duplicates a home directory, which is also how people move a machine.

ZCode is scoped by the database path combined with the raw session id, and the consequences are stated rather than smoothed over. A database recreated at the same path retracts the stale snapshots instead of mixing two histories, which is right. But moving the database to a different root leaves the old sessions in place until a forced rebuild, so a relocated database leaves duplicates behind that only a forced rebuild removes. The identity rules are the right ones for the common cases and they fail in the specified direction, which is more useful than a scheme that fails silently.

Replacement and deletion are replayed with provenance in mind, so a stale snapshot is retracted atomically rather than merged over, and compaction and branch summary model usage is folded into usage totals instead of being counted twice.

## Three named cases where the tool refuses to guess the session directory

Most indexers of this shape would infer the root and hope. This one names the cases where inference is unsafe and asks you to set it instead. Pi chooses its session directory in a stated order: `--session-dir`, then `PI_CODING_AGENT_SESSION_DIR`, then `sessionDir` in settings, then the default under `~/.pi/agent/sessions`. Obelisk follows absolute and `~` prefixed environment and global settings automatically, and follows the project setting for the cwd Obelisk itself was launched in, resolving a relative project setting against that cwd.

Then comes the limit, stated as three cases: CLI only roots, relative environment or global settings, and project settings belonging to another launch cwd cannot be inferred safely. In those cases the instruction is to select the resolved directory in Obelisk's Settings rather than letting Obelisk guess. OMP gets the same treatment in a shorter form: the default root is `~/.omp/agent/sessions`, and a custom absolute session directory has to be selected in Settings.

The practical consequence is that a working configuration is partly a configuration you filled in. A relative `sessionDir` in a project's settings file will be read correctly when Obelisk happens to start from that project and silently point elsewhere when it does not, because the relative path resolves against Obelisk's cwd rather than the agent's. That is a difference between a wrong answer and no answer, and the documentation chooses no answer.

GitHub Copilot is the one provider with automatic discovery of both VS Code Stable and Insiders user data roots, so nothing has to be selected for it unless a custom location is in use.

## Workflow tables fill in only where the provider emits workflow metadata

The schema has workflow tables and a subagents table, and both degrade for specific providers. Codex root threads become ordinary Obelisk sessions. Codex child threads are attached through the same `subagents` table, but only when parent thread metadata is available, and Codex does not emit Claude style workflow metadata, so workflow tables may be empty for Codex only history.

ZCode has the same shape of gap. Its legacy script workflow metadata is not indexed yet, so workflow child sessions appear as ordinary sessions. The distinction matters when you query: a child agent run that shows up as a plain session is still there and still searchable, but the parent relationship that would tell you it was spawned, and for what, is not available. Kimi is handled differently again: main and child agent `wire.jsonl` streams are both projected into the same messages, tools, summaries and subagents tables, with undo and clear handled as a full session replay so retracted wire records do not remain.

The adapter discipline is the other thing worth naming. Pi's session projection keeps the provider's tree, branch summaries, compactions, durable leaf, retained checkpoint tail, custom messages, bash records, tool calls, token usage and raw JSONL evidence inside the adapter, so no Pi specific database or renderer branch is needed elsewhere. Pi's own visibility rules are honoured: a retained tail replaces pre compaction ancestors even when those physical entries still exist, and entries the source explicitly superseded become `inactive`. OMP runs through a dedicated adapter with `source='omp'` and consumes OMP's mutable title prelude as session metadata while preserving the underlying tree. OMP and Pi roots are independent, so both histories index at once.

## Tests need an experimental node flag, and the build is three workspaces

The build is a workspace monorepo over `packages/*`, with named members for the core library, the CLI and a plugin, and the test script is not a bare invocation. A `pretest` hook builds the core, then the CLI, then the plugin workspace, so a test run that skips the build fails to find its own output. The test command itself is:

```
node --experimental-test-module-mocks --test tests/*.test.mjs
```

An experimental flag is in the test path, which is a deliberate trade for module mocking rather than a leftover. Type checking runs twice, once for the root and once against the app's own tsconfig, because the app is a separate compilation with its own settings. Linting is eslint with a flat config file, and there is a build script and publish script for the agent skill, driven by a script under `packaging/`.

There is also a script named `eval:context-window` pointing at `scripts/context-window-ab-eval.mjs`, which is the only measurement-oriented script in the manifest. An index that answers an agent's questions is only worth the tokens it costs, and an A and B evaluation of context window usage is how you would find out what this one costs.

The repository carries the agent facing surface in several places at once: a root `SKILL.md`, a `skill-doc/` directory, `maintainer-skills/`, a `skills-lock.json`, an `AGENTS.md`, a `CONTEXT.md` and a `PRODUCT.md`, plus an `install.sh` at the root. Two lockfiles for two ecosystems are present as well, `package-lock.json` alongside the skills lock, so the skill half of the project and the app half of it resolve dependencies separately.

## Conclusion

Obelisk is a good fit if you run several coding agents on one machine and want to ask questions across all of them, and the part that decides how much you should trust the answers is the provider table rather than the feature list. Read that table first. If most of your history comes from Claude Code, the source does not attest rewind or current leaf state, so superseded entries cannot be marked inactive and your results include work the agent threw away. If it comes from Hermes, compaction archived and rewound rows are both stored as inactive, so you cannot tell the two apart. If it comes from Pi, OMP or ZCode, the retraction is attested and the index is honest about it. And if your sessions live outside the default roots, set the directory in Settings rather than trusting the discovery, which is what the documentation tells you to do in exactly the cases where inference fails.

## FAQ

### Which coding agents does obelisk index?

Claude Code from `~/.claude/projects`, Codex from its sessions and archived sessions directories, GitHub Copilot from VS Code Stable and Insiders user data roots, DeepSeek Harness, Hermes Agent, Kimi Code, OMP, Pi and ZCode. All of them land in the same SQLite schema at `~/.obelisk/obelisk.sqlite` with a `source` value per row.

### Does obelisk know when an agent deleted or rewound its own work?

For some providers. Pi, OMP and ZCode attest inactive history from branch, leaf, compaction or rewind state. Claude Code's source does not attest rewind or current leaf state, so superseded entries cannot be marked, and Hermes stores both compaction archived and rewound rows as inactive without splitting them.

### What happens if my agent sessions are not in the default location?

Set the resolved directory in Obelisk Settings. The documentation states that CLI only roots, relative environment or global settings, and project settings from another launch cwd cannot be inferred safely, and an OMP custom absolute session directory also has to be selected there.

### Can obelisk read other agents' databases directly?

Yes, for the providers that keep one. ZCode's transcripts live in `~/.zcode/cli/db/db.sqlite` in WAL mode, opened read only with its sidecar watched, and Hermes is read from `~/.hermes/state.db` with its profiles directory.

### What licence is obelisk released under?

AGPL-3.0, recorded in both the project listing and the package manifest. The manifest root is marked private, so the published artefacts are the workspace packages and the skill rather than this package.

## Sources

- [License: AGPL-3.0](https://github.com/tommy0103/obelisk/blob/main/LICENSE)
- [Project website](https://obelisk.antinomie.org)
- [README](https://github.com/tommy0103/obelisk/blob/main/README.md)
- [Releases](https://github.com/tommy0103/obelisk/releases)
- [tommy0103/obelisk on GitHub](https://github.com/tommy0103/obelisk)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/tommy0103-obelisk
