Skip to main content
Glama
README.md
<p align="center">
  <img src="docs/banner.png" alt="Agent Session — universal session &amp; handoff layer for AI coding agents" width="80%">
</p>

<p align="center">
  <a href="https://github.com/anaknegeri/agent-session/releases/latest"><img src="https://img.shields.io/github/v/release/anaknegeri/agent-session?style=flat-square&color=2dd4bf&label=release" alt="Latest release"></a>
  <a href="https://github.com/anaknegeri/agent-session/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/anaknegeri/agent-session/ci.yml?branch=main&style=flat-square&label=ci" alt="CI"></a>
  <a href="go.mod"><img src="https://img.shields.io/badge/go-1.25-00ADD8?style=flat-square&logo=go&logoColor=white" alt="Go 1.25"></a>
  <img src="https://img.shields.io/badge/mcp-26%20tools%20%C2%B7%207%20resources-5eead4?style=flat-square" alt="MCP: 26 tools, 7 resources">
  <img src="https://img.shields.io/badge/deps-none-4ade80?style=flat-square" alt="No runtime dependencies">
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="MIT license"></a>
</p>

<p align="center">
  <a href="#install">Install</a> ·
  <a href="#quick-start">Quick start</a> ·
  <a href="#make-every-agent-use-agent-session-automatically">Wire your agents</a> ·
  <a href="#mcp">MCP</a> ·
  <a href="#token-savings">Token savings</a> ·
  <a href="#contracts">Contracts</a> ·
  <a href="ARCHITECTURE.md">Architecture</a>
</p>

---

**One session. Any coding agent.** Switch between **Claude Code, Codex, OpenCode**
— and whatever ships next — without re-explaining the work.

Agent Session is not another AI memory database. It is a **local, Git-aware session
layer**: agents are interchangeable workers, and the session is the portable state
of the work. One binary, one SQLite file per project, no daemon.

---

## Features

- **Local-first, zero-config** — single binary + SQLite. No PostgreSQL, Redis, Docker, Node or Python.
- **Agent-agnostic** — shared state over transcripts: task, decisions, progress, files, tests, blockers, next action. Adapters for Claude Code, Codex, OpenCode, Cursor, Cline, pi, omp (oh-my-pi).
- **Git-aware** — git is the source of truth for code; the session stores only state and context.
- **MCP-native** — 26 tools + 7 resources over stdio or streamable-http.
- **Human-readable** — `.agent/context/current.md` and deterministic handoff context, never locked in a proprietary DB.
- **Checkpoint & handoff** — snapshot the work, hand it to another agent deterministically.
- **Always available** — agents spawn the stdio MCP server on demand; optional user-scope registration.
- **Long-term memory** — knowledge store with SQLite FTS5 full-text search; auto-extracts decisions, resolved blockers and completed tasks from sessions (non-LLM).
- **Agent Plugin packaging** — ships as a portable plugin (agent-plugins.org v1.0.0).

---

## Install

Prebuilt binaries for **macOS, Linux and Windows** (`amd64` + `arm64`) are
published on every release. Pick whichever path is easiest for you.

### macOS & Linux

**Homebrew** (recommended on macOS):

```bash
brew install anaknegeri/tap/agent-session
```

**One-line curl installer** (installs both `agent-session` + `agent-session-mcp`,
auto-detects the latest version):

```bash
curl -fsSL https://raw.githubusercontent.com/anaknegeri/agent-session/main/scripts/install.sh | sh
```

Options: `AS_VERSION=0.1.2` to pin a version, `AS_INSTALL_DIR=~/.local/bin` to
install without sudo.

### Windows

**PowerShell** (installs to `%LOCALAPPDATA%\agent-session` and adds it to your PATH):

```powershell
irm https://raw.githubusercontent.com/anaknegeri/agent-session/main/scripts/install.ps1 | iex
```

### Any platform with Go

```bash
go install github.com/anaknegeri/agent-session@latest
```

### From a release

Grab the matching asset from the [latest release](https://github.com/anaknegeri/agent-session/releases)
(`agent-session-{os}-{arch}`, Windows uses `.exe`), then:

```bash
chmod +x agent-session-* && sudo mv agent-session-* /usr/local/bin/
```

### Update

```bash
brew upgrade agent-session        # Homebrew installs
agent-session update              # binary / curl installs (auto-checks GitHub releases)
```

### From source

Requires only **Go binary + Git**:

```bash
make build             # bin/agent-session + bin/agent-session-mcp
make install           # copies to ~/.local/bin
make cross-compile     # dist/ for all 6 platform/arch combos
```

### Global availability in Claude Code

Register at **user scope** so `agent-session` appears in `claude mcp list` from every directory:

```bash
agent-session plugin install claude --scope user
```

---

## Quick start

<p align="center">
  <img src="docs/demo.gif" alt="One take: agent-session init wires every installed agent, start opens a session, status shows tasks and decisions, handoff hands the whole thing to Codex" width="100%">
</p>

<p align="center">
  <sub><code>init</code> wires every agent on the machine · <code>status</code> shows what the last agent recorded · <code>handoff</code> passes the session on. Rendered from <a href="docs/demo.tape">docs/demo.tape</a>.</sub>
</p>

```bash
cd your-project
agent-session init                # one command: init + wire agents at user scope
agent-session start "Implement OAuth2 PKCE"
agent-session status              # progress, blocked, next
agent-session checkpoint --next-action "Fix refresh token rotation"
agent-session handoff codex       # deterministic context for the next agent
agent-session resume --agent opencode
agent-session history             # event log
agent-session context             # print context.md
```

`agent-session init` is the one-command entrypoint per project, modeled after
`git init`: it initializes the project (creates `.agent/`, starts a session) and
wires AGENTS.md plus all agents. It is idempotent — safe to re-run.
`agent-session setup` is an alias. Add `--no-agents` for the session layer only,
or `--only claude|opencode|codex` to wire a single agent.

> **Not a git repo yet?** `init` reminds you to run `git init` first.
> The session works in local-only mode meanwhile, but Agent Session is Git-aware
> and tracks branch/commit/diff once git exists.

---

## CLI reference

| Command | Description |
|---|---|
| `agent-session init` | One-command setup: init + wire agents at user scope (`--only`, `--no-agents`, `--project`). Alias: `setup` |
| `agent-session start [title]` | Start a new session (`-t, --title`) |
| `agent-session status` | Show current session state |
| `agent-session ui` | Full-screen dashboard: tasks, progress, decisions, blockers, events, tests |
| `agent-session stats` | Session statistics: task completion, decisions, blockers, tests, agents used |
| `agent-session timeline` | Visual event timeline with icons (`-n, --limit`) |
| `agent-session diff` | Show what changed between two checkpoints (defaults to two latest) |
| `agent-session projects` | List all registered projects with session status (`--prune` to clean stale) |
| `agent-session export` | Export session to JSON or Markdown (`-f, --format`, `-o, --output`) |
| `agent-session import <file>` | Import a session from a JSON export |
| `agent-session watch` | Auto-regenerate context.md when the session changes (`-i, --interval`) |
| `agent-session resume` | Resume the latest session (`-a, --agent`) |
| `agent-session checkpoint` | Create a checkpoint (`--label`, `-n, --next-action`) |
| `agent-session handoff <agent>` | Compose handoff context for `claude`/`codex`/`opencode`/`pi`/`omp` |
| `agent-session history` | Recent events |
| `agent-session context` | Print context (`-d, --depth summary|recent|full`) |
| `agent-session doctor` | Health check: project, session, store, git |
| `agent-session mcp` | Run the MCP server (`--transport stdio|streamable-http`, `--addr auto|host:port`) |
| `agent-session plugin pack` | Build the Agent Plugin package |
| `agent-session plugin install <agent>` | Wire an agent: `claude`, `codex`, `opencode`, `cursor`, `cline`, `pi`, `omp` (`--scope project|user` for claude, opencode, cursor, pi, omp) |
| `agent-session plugin uninstall <agent>` | Remove the wiring |
| `agent-session setup` | Wire agents at user scope + AGENTS.md for always-on behavior (`--only`, `--project`) |
| `agent-session task add\|list\|update` | Record tasks from the CLI (the MCP `task.*` tools without MCP) |
| `agent-session decision add\|list` | Record a decision (`-r, --reason`) |
| `agent-session blocker add\|list\|resolve` | Record and resolve blockers (`--open`) |
| `agent-session event add <type>` | Append a canonical event (`-p, --payload`) |
| `agent-session memory put <content>` | Store knowledge (`-k kind`) |
| `agent-session memory list` | List knowledge (`-k kind`, `-n limit`) |
| `agent-session memory search <query>` | Full-text search knowledge |
| `agent-session memory promote` | Promote session decisions/blockers/tasks into memory |

---

## Make every agent use Agent Session automatically

One command per project — it initializes if needed and wires the agent-session
MCP server **once at user scope**, so your project stays clean:

```bash
cd your-project
agent-session init
```

What `init` does (idempotent, safe to re-run):
- **`.agent/`** — project session state (required, per-project by design).
- **AGENTS.md** — appends a mandatory workflow section read by OpenCode, Codex,
  Cursor and Claude Code: agents first call `session.get` + `context.get`, track
  work with `task.*` / `decision.*` / `blocker.*` / `event.append`, and create a
  checkpoint before finishing.
- **User-scope MCP registration** (only for agents installed on this machine,
  no per-project config files created):
  - **Claude Code** — `claude mcp add --scope user agent-session`
  - **OpenCode** — `~/.config/opencode/opencode.json`
  - **Codex** — `codex mcp add agent-session` (global by default)
  - **Cursor** — `~/.cursor/mcp.json`
  - **Cline** — no user scope; wire per-project with `--only cline`
  - **pi** — `~/.pi/agent/` extension + skill + prompts. pi ships **no MCP**
    ("No MCP. Build CLI tools with READMEs, or build an extension that adds MCP
    support"), so it is wired through the CLI instead: an extension resumes and
    checkpoints on pi's own lifecycle events, and a skill teaches the model the
    `task` / `decision` / `blocker` / `event` verbs. Nothing MCP is registered.
  - **omp (oh-my-pi)** — `~/.omp/agent/mcp.json` + extension + skill + slash
    commands. omp is a pi derivative that *does* ship an MCP client, so it gets
    both halves: the MCP server for tool calls, and an extension that resumes on
    `session_start` and checkpoints on `session_before_compact` /
    `session_shutdown` so continuity does not depend on the model remembering.
    Note the roots — omp reads `.omp/` and `~/.omp/agent/`, never `.pi/`, so pi's
    wiring does nothing here. Named profiles (`omp --profile x`) read
    `~/.omp/profiles/x/agent/` and are not covered by user-scope wiring.

The MCP server resolves the project root from the working directory, so the
same user-scope registration works in every project, with no per-project
config pollution.

`init` also installs **slash commands** at user scope (Claude Code
`~/.claude/commands/`, OpenCode `~/.config/opencode/commands/`, Cursor
`~/.cursor/commands/`, pi `~/.pi/agent/prompts/`, omp `~/.omp/agent/commands/`) so
you can run `/agent-session`, `/agent-session-checkpoint`, and
`/agent-session-record` in any project. `plugin uninstall <agent> --scope user`
removes them. pi gets its own wording of the three: the shared set tells the agent
to call MCP tools, which pi has none of — omp uses the shared set, because it has.

Prefer per-project wiring? Use `agent-session init --project`, or `--only <agent>`
to wire a single agent into this project only. `--no-agents` keeps the session
layer only.

---

## MCP

### "Does the MCP server need to be running?"

No. With **stdio** (the default used by the adapters), Claude Code / OpenCode /
Codex **spawn the server automatically** as a subprocess on demand and shut it
down when the session ends. There is no daemon to manage.

With **streamable-http** the server is a persistent daemon. Auto-start templates:

- macOS: `packaging/launchd/com.agent-session.mcp.plist.tmpl`
- Linux: `packaging/systemd/agent-session-mcp.service.tmpl`

The daemon picks a **unique per-project port** (hash of the project root in
`45100–47099`, with free-port fallback):

```bash
agent-session mcp --transport streamable-http        # e.g. http://127.0.0.1:46688/mcp
agent-session-mcp --transport streamable-http --addr auto
```

In a project that is not yet initialized, the server stays connected and tools
return `project not initialized, run agent-session init` instead of a connection failure.

### Tools

`session.get`, `session.checkpoint`, `session.diff`, `session.resume`, `session.record`,
`context.get`, `context.read`, `context.update`, `context.summarize`,
`task.create`, `task.get`, `task.update`, `decision.list`, `decision.create`,
`blocker.create`, `blocker.list`, `blocker.resolve`, `event.append`,
`workspace.status`, `workspace.diff`,
`memory.put`, `memory.get`, `memory.search`, `memory.delete`, `memory.promote`

`session.record` is the unified way to log work — it appends an event, records a
decision, sets `next_action`, and/or creates a checkpoint in a single call.


### Resources

`session://current`, `session://context`, `session://decisions`, `session://tasks`,
`session://workspace`, `session://checkpoint/latest`, `memory://recent`

### Canonical events

`session.started`, `task.created`, `task.updated`, `file.changed`, `command.executed`,
`test.started`, `test.failed`, `test.passed`, `decision.created`, `blocker.created`,
`checkpoint.created`, `handoff.created`, `session.completed`

Large event payloads (>8KB) are stored as artifacts and referenced by `artifact_id`.

### Long-term memory (Phase 4)

Memory is separate from session state. The `knowledge` table is a
long-term store with SQLite FTS5 (porter tokenizer) full-text search:

- **Kinds** — `project_knowledge`, `architecture`, `solution`, `preference`, `skill`
- **Manual** — `memory.put` by the agent or `agent-session memory put`
- **Auto-promote** (non-LLM) — `memory.promote` extracts structured session state:
  `decision → architecture`, `resolved blocker → project_knowledge`, `completed task → solution`.
  Idempotent — already-promoted entities are skipped.

---

## Token savings

Agent Session is designed to keep prompts small (state over transcript):

- **Progressive context loading** — `context.get` defaults to a bounded summary,
  not full history.
- **Context budget** — configurable limits (`[context]`): max decisions/blockers/
  files/events/progress, per-item truncation, and a hard total clamp
  (`max_total_chars`). Lists render as `… +N more`.
- **Relevant memory injection** — `context.get` automatically surfaces top-k
  related knowledge (local FTS search of the current task) so agents don't
  re-derive known facts.
- **Tool annotations** — MCP tools carry `readOnlyHint` /
  `destructiveHint` / `idempotentHint` where they hold, so agents pick the right
  tool without trial-and-error. Tools that create a record carry none: a repeat
  call creates a second one.
- **PreCompact checkpoint** — Claude Code saves a checkpoint before context
  compaction, so nothing is lost when the window shrinks.
- **Agent-driven summarizer** — `context.summarize` asks the agent itself to
  write a short session summary and store it via `memory.put` (kind
  `project_knowledge`). No external LLM API needed.
- **Artifacts** — large event payloads are stored as references, not inline.

Tune in `.agent/config.toml`:

```toml
[context]
max_decisions  = 5
max_blockers   = 3
max_files      = 8
max_events     = 10
max_progress   = 10
max_item_chars = 200
max_total_chars = 4000
inject_memory  = true
max_memory     = 3

# How many checkpoints of each kind a session keeps. Limits are per kind so a
# burst of automatic checkpoints cannot evict the deliberate ones. 0 = unlimited.
[retention]
max_manual     = 50
max_auto       = 20
max_precompact = 10
max_handoff    = 20
```

Every checkpoint records what triggered it — `manual`, `auto` (Stop hook or a
stale context), `precompact`, or `handoff` — and retention is applied per kind.
The session's most recent checkpoint is never pruned.

### Benchmark

Reproducible benchmark at `bench/token-benchmark.sh` measures the real output
size (in characters) of context-gathering operations, converted to approximate
token cost (chars ÷ 4). Token estimates use the standard English-text heuristic;
your model's exact tokenizer may differ.

**Methodology** — "Without" = manual exploration an agent must do on every cold
start / post-compaction / handoff (read README, AGENTS.md, `git log`, `git status`,
`git diff`, glob source files). "With" = a single `context.get` call.

#### Results (live run on this repo, 2026-08-12)

| Scenario | Without agent-session | With agent-session | Savings |
|---|---|---|---|
| Cold start (first turn) | ~10,146 tokens | ~773 tokens | **92%** |
| Post-compaction re-orientation | ~10,146 tokens | ~773 tokens | **92%** |
| Agent handoff (Claude → Codex) | ~10,146 tokens (state lost) | ~1,195 tokens (state preserved) | **88%** |

#### Cumulative savings over multiple re-orientations

| Re-orientations | Without | With | Saved |
|---|---|---|---|
| 1 | 10,146 tok | 773 tok | 9,373 tok |
| 5 | 50,730 tok | 3,865 tok | 46,865 tok |
| 10 | 101,460 tok | 7,730 tok | **93,730 tok** |
| 20 | 202,920 tok | 15,460 tok | 187,460 tok |

#### Context depth comparison

| Depth | Chars | ≈ Tokens |
|---|---|---|
| `summary` (default, clamped at 4000 chars) | 3,094 | 773 |
| `recent` (bounded lists, no hard clamp) | 2,983 | 745 |
| `full` (never truncated) | 5,894 | 1,473 |

The three depths select different content, so `recent` is not simply `summary`
plus more — which is why it can measure slightly smaller:

- `summary` — bounded lists, the `max_total_chars` clamp, and nudges.
- `recent` — the same list bounds, no total clamp, no nudges, plus a recent-events
  section.
- `full` — no bounds at all, plus recent events.

Which one is larger therefore depends on whether the session's nudges outweigh
its event list, and both can vary per repository and per session.

> "Without agent-session" is a **minimum** — in practice agents also search code,
> read specific files, and re-derive decisions that agent-session preserves
> structurally. Real-world savings are higher.

Reproduce:

```bash
./bench/token-benchmark.sh
```

---

## Project discovery

The MCP server is registered once at user scope, so it can start in a directory
that has nothing to do with the project being worked on. The project root is
resolved in this order:

1. `$AGENT_SESSION_PROJECT`, when it names a directory that exists
2. the nearest ancestor containing `.agent/`, searching no further up than the
   git repository that contains the starting directory
3. the git repository root, when there is no `.agent/` inside it yet
4. the starting directory

The search stops at the repository boundary on purpose. Running `agent-session
init` in `$HOME` once puts a `.agent/` above every repository, and without that
boundary any uninitialized repo underneath resolved to that unrelated project and
recorded its session there. Outside a git repository there is no boundary to
stop at, which is what `AGENT_SESSION_PROJECT` is for:

```bash
AGENT_SESSION_PROJECT=/path/to/project agent-session context
```

---

## Automatic recording & nudges

Agent Session is designed so agents never have to remember to record state:

- **Auto-record file changes** — `context.get` compares git status against
  recorded events and appends `file.changed` for anything new, so file edits
  are captured even if the agent never calls `event.append`.
- **Auto-checkpoint when stale** — if no checkpoint exists in the last 10
  minutes and the git tree is dirty, `context.get` creates one automatically.
- **Context nudges** — the summary view warns about stale checkpoints
  (>30 min), unrecorded changed files, and open blockers, so the agent knows
  when to checkpoint.
- **`session.record`** — record an event, a decision, `next_action`, and/or a
  checkpoint in one tool call instead of several.

---

## Configuration

`.agent/config.toml` — zero-config defaults, human-editable:

```toml
[project]
name = "sakreta"

[storage]
driver = "sqlite"

[session]
auto_checkpoint = true      # checkpoint after task/decision/test events
smart_checkpoint = true     # rate-limit auto-checkpoints (max 1 per 60s)

[git]
enabled = true

[agents.claude]
enabled = true

[agents.codex]
enabled = true

[agents.opencode]
enabled = true

[sync]
mode = "local-only"         # local-only | git-sync | cloud-sync (phase 2)
```

---

## Local data layout

```
project/
└── .agent/
    ├── config.toml           # configuration
    ├── session.db            # SQLite: projects, sessions, tasks, decisions,
    │                         #   blockers, session_events, checkpoints,
    │                         #   agent_sessions, artifacts, knowledge (+FTS)
    └── context/
        └── current.md        # human-readable current state (re-generated)
```

Everything stays local. Add `.agent/` to `.gitignore`, or commit it later for git-sync (phase 2).

`config.toml` records the format version (`[format] version = 1`) so a future build
can tell whether it understands the layout — see
[docs/spec/format-v1.md](docs/spec/format-v1.md).

---

## Security & privacy

- **Local only by default** — no data leaves the machine.
- A shared session is **untrusted context**. Any agent can write to it, so
  everything an agent authored — task titles, decisions, blockers, event payloads,
  memory — is rendered under an `(untrusted)` heading with a legend saying it is
  data and not instructions, and is flattened to a single line first so it cannot
  forge a section of its own. The MCP server states the same rule in its
  initialization instructions.
- No credentials or secrets are stored by the session layer.

---

## Contracts

A session is written by one agent and read by another, often by a different build
of this binary. Seven interfaces cross that boundary and are specified in
[`docs/spec`](docs/spec/README.md), all at **v1**:

| Contract | Covers |
| --- | --- |
| [Agent Session Format](docs/spec/format-v1.md) | the `.agent/` layout, ID prefixes, timestamp encodings |
| [Checkpoint Schema](docs/spec/checkpoint-v1.md) | the snapshot JSON inside a checkpoint |
| [Event Schema](docs/spec/event-v1.md) | event types and their payloads |
| [Context Schema](docs/spec/context-v1.md) | the rendered context document |
| [Handoff Schema](docs/spec/handoff-v1.md) | the handoff document and its event |
| [MCP Tool Contract](docs/spec/mcp-tools-v1.md) | the 26-tool / 7-resource surface and its annotations |
| [Export Schema](docs/spec/export-v1.md) | the `export`/`import` document |

The versions live in [`pkg/contract`](pkg/contract/contract.go) and every one of
them is held to its spec by [`test/contract`](test/contract), so drift fails the
build instead of surfacing later as an agent misreading a session. `.agent/`,
each checkpoint and each export document record the version they were written
against, and a build refuses to read a version it does not understand rather
than guess.

The rule: **bump for any change that could make an existing reader wrong**, no bump
for additive change. See [docs/spec/README.md](docs/spec/README.md).

---

## Development

```bash
make build     # build both binaries
make test      # go test ./...
make vet       # go vet ./...
make plugin    # package the Agent Plugin
```

Layout: clean architecture (`Domain → Application → Infrastructure`) with Wire DI.
See `ARCHITECTURE.md` for the full schema, MCP design and development order.

Testing: SQLite `:memory:` unit tests, real-git integration fixtures, in-process
MCP client tests, real MCP-over-stdio integration tests, and a live end-to-end
flow (`init → task → checkpoint → handoff → resume`). Real-agent smoke tests
(Claude Code / Codex / OpenCode) run with `AGENT_SESSION_SMOKE=1` where the
agent CLIs are installed. See `test/integration/COMPATIBILITY.md` for the
agent capability matrix.

---

## License

MIT — see [LICENSE](LICENSE).

TDQS

A3.9/5.0

Scored across 26 tools

Disambiguation4/5

The set has several intentionally overlapping pairs (context.get vs context.read, session.record vs event.append/decision.create/context.update, session.checkpoint vs session.record, memory.put vs memory.promote), but each description explicitly states when to prefer which, which keeps misselection low. A few boundaries (e.g. context.update task fields vs task.update) still require reading two descriptions to resolve, so it is not a clean 5.

Naming Consistency5/5

Every tool follows a strict dotted `resource.action` convention (artifact.get, decision.list, blocker.resolve, context.summarize, memory.search, session.checkpoint, task.update, workspace.diff). Namespaces are used consistently and verbs are predictable across all 26 tools.

Tool Count3/5

26 tools is heavy for a session/context-tracking server, and several exist mainly as thin duplicates of others (context.get/context.read, context.update vs task.update, memory.promote vs memory.put, session.record vs the individual record tools). The surface is defensible but feels broader than the domain warrants.

Completeness3/5

Core lifecycle is well covered (events, decisions, blockers, tasks, checkpoints, memory, workspace), but there are notable gaps: no event.list/query despite an append-only log (events are only visible via context.summarize's last 8), no task.list, and no way to update or delete a decision or edit a blocker besides resolve. Agents will hit dead ends when they need to inspect history or correct prior entries.

Maintenance

ActivityStale
ResponsivenessNo issues