agents-cli · sessions · architecture
Agents (Claude, Codex, Grok, Kimi…) do not share a brain. Each conversation is a transcript
file on disk. agents sessions builds a per-machine SQLite + FTS5 index of those
files, then answers filtered queries — locally, or on other machines over SSH. This page is
grounded in the agents-cli source (lib/session/*).
When someone asks “how do agents share memory across devices?”, the honest answer is: they don’t. They query.
agents sessions "topic" --since 7d (or reading a session id as markdown) — the same family of operation as grepping logs, not loading a shared embedding space.
Every machine that runs agents sessions maintains its own index. Discovery walks agent homes; only changed files are re-parsed; results land in SQLite.
Specs live in lib/session/discover.ts as SESSION_ROOT_SPECS:
Claude → projects, Codex → sessions, Gemini → tmp, Kimi → sessions, Droid → sessions, etc.
getAgentSessionDirs() expands each to:
~/.claude/projects~/.agents/versions/<agent>/<ver>/home/…~/.agents/.history/backups/<agent>/<machine>/…Expose the live set with agents sessions --roots --json so external watchers stay in lockstep with the CLI.
tryClaimScan(pid) so only one process scans; others serve the existing DB (db.ts).~/.claude/… and versioned homes of the same file don’t double-parse.Database path: ~/.agents/sessions/sessions.db (WAL, busy_timeout 30s). Defined in lib/session/db.ts.
| Table | Role |
|---|---|
sessions | Metadata: id, agent, project, cwd, topic, label, cost, tokens, PR/ticket, file_path, timestamps |
session_text (FTS5) | Full-text: label, topic, project, content — BM25 weights label > topic > project > content |
scan_ledger | mtime/size/scanned_at per file — “did we already look at this?” |
meta | schema version, scan_in_progress claim, migration flags |
// Query path after scan (discoverSessions) getDB(); // open/migrate sessions.db tryClaimScan(pid) → scan agents // incremental only sessions = querySessions(filters) // SQL WHERE + ORDER + LIMIT // Text search: hits = ftsSearch(query) // label tiers + FTS5 BM25
agents sessions calls stay sub-second once the ledger is hot.
There is no multi-master memory. Cross-device is run the same query on the peer, over SSH, and merge rows tagged by machine.
machine + _remote: true so resume/read route back over SSH.--local | This machine’s disk + DB only |
--host zion | Explicit peer(s); strip --host, forward filters |
| default list | Online devices from agents devices |
| import --from-host | Copy transcripts into local mirror |
| sessions sync | Optional R2 CRDT — opt-in beta |
AGENTS_SESSIONS_LOCAL=1ensureWholeIndex adds --all (remote cwd is home, not your project)~/.agents/.cache/remote-sessions/ for explicit --host replays# Explicit peer — runs peer's own index agents sessions --all --since 7d --host zion --flat # Fan-out (interactive default) — merge all online machines agents sessions --all --since 1d --flat # Stay local (fast scripts / JSON) agents sessions --all --since 7d --local --json -n 50 # Copy a peer week into a local mirror (tagged by origin machine) agents sessions import --from-host zion --since 7d
unreachable or no agents CLI). There is no alternate memory channel.
Optional R2 session-sync is a separate opt-in path — off by default on most fleets.
Filters compile to SQL WHERE clauses in buildSessionWhere and/or FTS MATCH. Same flags work local and remote (forwarded over SSH).
| Filter | Effect | Layer |
|---|---|---|
"search text" | Label-first tiers + FTS5 BM25 over content | FTS |
--since 7d / --until | timestamp window | SQL |
-a claude / --codex | agent (optional @version) | SQL |
-p agents-cli | project substring | SQL |
--all | drop cwd scoping | query scope |
--teams | include team-origin (hidden by default) | SQL flag |
--active | live processes (separate path + remote-active fan-out) | process scan |
-n 50 · --sort cost | limit + order | SQL |
--include user · --last 3 | when rendering one session | render |
--json / --markdown / --flat | output shape | CLI |
--artifacts | files written during the session | parse |
--host / --device | where the query executes | remote |
# Agent mid-turn recall pattern agents sessions --all --since 7d "secrets unlock" --flat -n 20 agents sessions a1b2c3d4 --markdown --include user,assistant --last 5 agents sessions --active --host zion
Wall-clock on yosemite-s0 (agents-cli 1.20.x), three warm runs each. Local = index hit. Host = SSH + peer index.
| Call | Times (3×) | ≈ |
|---|---|---|
--local --json --since 7d -n 50 | 0.47 · 0.45 · 0.45 s | ~0.45s |
--local --flat --since 7d -n 50 | 0.85 · 0.78 · 0.83 s | ~0.8s |
search "auth" local 7d | 0.84 · 0.80 · 0.85 s | ~0.8s |
--active --local | 0.63 · 0.62 · 0.64 s | ~0.6s |
--markdown --last 3 one id | 0.50 · 0.50 · 0.50 s | ~0.5s |
--host yosemite-s1 1d -n 20 | 0.74 · 0.77 · 0.74 s | ~0.75s |
| default multi-host fan-out 1d | 1.81 · 1.80 · 1.81 s | ~1.8s |
Investigated on yosemite-s0 after a real agents sessions call. Path from lib/state.ts: ~/.agents/.history/sessions/sessions.db.
| Fact | Value |
|---|---|
| Path | ~/.agents/.history/sessions/sessions.db |
| Schema version | 12 (meta table) |
| Journal | WAL · busy_timeout 30s · concurrent writers OK |
| Core tables | sessions, session_text (FTS5 + shadow tables), scan_ledger, meta |
| sessions columns | id, short_id, agent, version, account, timestamp, last_activity, project, cwd, topic, label, message_count, token_count, cost_usd, duration_ms, file_path, pr_*, ticket_id, plan, … |
# Inspect yourself sqlite3 ~/.agents/.history/sessions/sessions.db \ "SELECT COUNT(*) FROM sessions; SELECT value FROM meta WHERE key='schema_version';" # FTS smoke test (same engine ftsSearch uses) sqlite3 ~/.agents/.history/sessions/sessions.db \ "SELECT session_id FROM session_text WHERE session_text MATCH 'auth*' LIMIT 5;"
agents sessions on the receiving machine re-indexes them.
Yes. Explicit hand-off without requiring R2 sync. Format: NDJSON bundle (lib/session/bundle.ts, RUSH-1710/1711).
| Command | What it does |
|---|---|
agents sessions export --since 7d -o week.bundle | Select via same filters as list; write NDJSON archive |
export … --stdout | Pipe over SSH |
export … --encrypt | AES-256-GCM per body |
export --host zion … | Run export on peer, stream bundle back |
import week.bundle | Place into backups/<agent>/<originMachine>/… |
import --dry-run | Plan only: new / dup / conflict |
import --from-host peer --since 7d | export on peer + import here in one shot |
import - --decrypt | stdin pipe + decrypt |
# Proven on yosemite-s0 just now agents sessions export --all --since 7d --claude -n 2 -o /tmp/demo.bundle # → Exported 2 sessions (2 files, redacted) # header: kind=agents-session-bundle version=1 redacted=true agents sessions import /tmp/demo.bundle --dry-run # → 2 sessions · status new · (dry run — nothing written) # One-liner peer pull agents sessions import --from-host zion --since 7d # Encrypted pipe agents sessions export --since 7d --stdout --encrypt \ | agents ssh boxB 'agents sessions import - --decrypt'
--host / fan-out) never copies files — peer answers from its own index.
Import materializes transcripts under a machine-tagged mirror so you can search them offline later.
Neither is “shared memory”; both are deliberate tools.
| File | Responsibility |
|---|---|
| commands/sessions.ts | CLI entry, flags, listing UI, routing to local/remote |
| lib/session/discover.ts | Transcript roots, per-agent scanners, incremental filter, machine tagging |
| lib/session/db.ts | SQLite schema, scan ledger, querySessions, FTS5, scan claim |
| lib/session/remote-list.ts | Default listing fan-out: SSH peers, merge SessionMeta[], _remote |
| lib/session/remote.ts | Explicit --host: forward args, ensureWholeIndex, cache, outcomes |
| lib/session/remote-active.ts | Fan-out for --active |
| lib/session/sync/* | Optional R2 CRDT mirror (beta, off by default) |
| lib/session/bundle.ts | export / import portable archives |
# Prove the index roots on any machine agents sessions --roots --json --local # Prove cross-device is live query, not sync agents sessions sync --status # → often: automatic sync disabled · no shared cloud brain
agents sessions is the query tool. Cross-device = SSH the same query to the peer’s index.
Fast enough to use mid-turn. Not shared memory.