ai-jail integration (`ai-memory run --yolo`):
- The re-exec built `ai-jail <flags> <exe> run …` with no `--`. ai-jail
rejects one of its own flags after the command and `run` shares flag names
with it, so `run claude --yolo --env GH_TOKEN=…` aborted. The invocation now
emits `--` before the wrapped exe (forwarding a colliding flag additionally
needs ai-jail >= 2.4.2, whose guard honors the separator; the cross-tool
test gates on that version).
- The offer only checked for a file named ai-jail: Windows could show it, a
host without bwrap/sandbox-exec was offered a jail that cannot start, and a
~/.local/bin-only install was offered and then not found by the bare
`Command::new("ai-jail")` re-exec after the run was already cancelled.
usable_ai_jail(os, lookup) now returns the exact binary to exec only on
Linux/macOS with the backend present; otherwise no question is asked.
--true-yolo:
- It now implies --yolo (warning, ai-jail offer, harness dangerous mode):
alone it used to apply Claude's bypassPermissions with no warning. It is
interchangeable with --yolo for non-Claude harnesses, and recognized after
native arguments (`run claude --model opus --true-yolo`), where clap leaves
it in the native argv and it was forwarded to Claude as an unknown option.
- The claude_true_yolo config key only upgrades an explicit yolo launch, as
its doc comment stated, instead of bypassing permissions on every run.
- Removed what never worked: three CLAUDE_CODE_DISABLE_*RM* env vars Claude
Code does not read (absent from the 2.1.280 binary and its env reference),
and an empty permissions.ask array that cannot clear ask rules from other
scopes (Claude unions them). Docs now state that Claude honors explicit ask
rules and its command-safety checks in every permission mode.
Relaunch after an interrupted run:
- A launcher killed before releasing its lease (terminal closed, ai-jail
torn down) left the workstream held for up to 90s and the next launch failed
after a 5s retry. An interactive launch now parses the holder and expiry from
the 409, waits for that lease to lapse (bounded by one lease; Ctrl-C aborts),
then proceeds. A holder that renews meanwhile is reported as live and never
displaced; the server's busy check stays the only arbiter (security
inventory row 13b). Non-interactive launches keep the short window.
Tests: usable_ai_jail OS/backend/Windows/exact-path, `--` placement and the
colliding-flag regression (unit + real ai-jail --dry-run), the yolo_modes
table, --true-yolo in both argv positions via real clap parses, the reduced
true-yolo argv, and HTTP-level held-lease wait / renewed-owner / Ctrl-C cases.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
16 KiB
ai-memory cookbook
A task-oriented cheat sheet: "I want to do X" → how. For the full reference see
ARCHITECTURE.md; for install see install.md
(including the macOS menu bar app);
for the tool-routing table see usage.md.
What ai-memory is, in one paragraph
ai-memory gives your AI coding agents long-term, cross-session memory. It runs
as one server; your agent talks to it over MCP (tools like memory_query,
memory_write_page) and over lifecycle hooks that automatically capture what you
do. Memory is a markdown wiki in git (the source of truth, hand-editable) plus
a derived SQLite index for search. Everything is scoped per project
(workspace, project), resolved from your working directory. It works with no
LLM at all (capture + full-text search + rule-based summaries); adding a provider
enables consolidation and auto-improvement.
Everyday tasks (through your agent, over MCP)
You mostly just talk to your agent; it calls the right tool. Common ones:
| You want | Say to your agent | Tool it uses |
|---|---|---|
| Recall prior work | "Have we discussed X?" / "what did we decide about Y?" | memory_query |
| Catch up after time away | "Where did we leave off?" / "catch me up" | handoff block / memory_explore |
| Remember a durable fact | "Remember that we always use pnpm here" | memory_write_page |
| Remember until a date | "Remember this until the migration ships" | memory_write_page + expires_at |
| A standing rule for every project | "Always: never force-push" | memory_write_page with scope: "global" |
| Read a specific saved page in full | "Read the _rules/deploy page" |
memory_read_page |
| Save context for the next session | "Wrap up — save context for next time" | memory_handoff_begin |
| See project stats | "How much memory do we have?" | memory_status / memory_briefing |
You rarely write pages by hand: lifecycle hooks capture prompts and tool calls automatically, and (with an LLM) consolidation compiles them into pages.
Recipe: keep a rule a project must follow
"Remember: before touching the payment code, read the PCI notes."
Your agent calls memory_write_page and it lands as a durable wiki page (routed
under _rules/ when it's a rule). Next session, memory_query surfaces it, and
if the project's .ai-memory.toml opts into the on-start brief, rules are
prepended to the agent's context automatically. To make it apply to all your
projects, ask for it as a global rule (scope: "global").
Recipe: have a project read a specific document before implementing
Two ways, depending on where the document lives:
- It's already a wiki page (you saved it, or imported it — below): tell the
agent "read the
norms/gdpr-retentionpage before implementing" — it callsmemory_read_pagewith that path and works from the full body. - It's an external file (a norm/spec on disk): save it as a page first
("remember this spec as
norms/gdpr-retention", paste or point at it), then reference it as in (1). Large references are best split into a page per document so retrieval can pull just the relevant one.
Recipe: import an existing knowledge base (e.g. OKF norms/laws/specs)
ai-memory's wiki is OKF (Obsidian-compatible markdown + frontmatter). To bring an existing body of documents in as project memory:
- A few documents: save each as a durable page via your agent
(
memory_write_page) or the CLI (ai-memory write-page), one page per document, under a stable prefix likenorms/…. Then any project can read a specific one before implementing (recipe above). - A whole OKF/Obsidian vault or an export from another tool: use the
companion importer (see
companion-crates.md) — it ingests OMC and external-conversation exports into the store.ai-memory export-okfis the inverse (export your wiki as an OKF bundle), useful to see the exact on-disk shape your documents should take. - The wiki lives at
<data_dir>/wiki/<workspace>/<project>/…and is plain markdown in git, so you can also drop files in and let the file watcher + reindex pick them up. Keep them under a clear prefix and commit.
Once the material is saved as pages in the right scope, any project can search
it (memory_query) and read a specific document in full (memory_read_page)
before implementing against it.
Recipe: control what gets kept, aged, or consolidated
By default nothing you have to think about: memory decays on a single gentle
curve, and an upgrade to 2.4 changes no scores and evicts nothing. When you do
want to tune aging, it is all opt-in and reversible — the original of anything
compacted or merged stays in git and the supersession chain, recoverable with
ai-memory restore-page.
- Keep something forever: pin it. A pinned page is exempt from the forget-sweep regardless of tier or age. Semantic and procedural pages never decay either — only working/episodic memory ages.
- Make a note expire on a deadline: ask your agent to remember it "until
" (an
expires_at); the forget-sweep deletes it when the time passes, no matter how often it was read. A TTL outranks pinning. - Tune how long each tier lasts: set per-tier half-lives in the project's
.ai-memory.toml[decay.half_life_days](e.g. keep episodic history longer, working-tier scratch shorter). Omit it for the default single curve. - Compact instead of evict, and de-duplicate:
[decay] compact_cold_episodickeeps a cold page's durable facts (paths, error codes, decisions) and drops the prose;[decay] dedup_cold_clusterscollapses near-duplicate cold pages into one survivor. Both are zero-LLM, off by default, and supersede rather than delete. - Keep used memory longer: nothing to configure — a page you open, search, or reach through a related-pages walk is reinforced automatically and resists decay.
- Surface likely contradictions: run
memory_lint(through your agent or the CLI); with embeddings configured it flags pairs of same-topic pages that look like they conflict, advisory only. Similarity reads shared vocabulary as much as disagreement, so a single-domain or single-language store yields mostly candidate pairs: read each finding as a pair to check, not as a defect. If that noise is too high, raisecontradiction_band_min(config.tomlorAI_MEMORY_CONTRADICTION_BAND_MIN, default0.4) — on such a store the band measures domain proximity more than conflict, so a higher floor trims same-domain-but-unrelated pairs. - Let an LLM consolidate on idle ("dream"): with a provider and an embedder
configured,
[dream] enabledturns on a background pass that rewrites clusters of cold notes into single coherent pages while you're idle and cancels the moment you return. It is off by default, never deletes a source, and is gated on an internal recall eval before it could ever become default behavior.
Recipe: two agents / two repos working together
- Continuity across agents in the same project (quit Claude Code, open Codex in the same repo): automatic. A handoff is captured at session end and the next session's on-start hook prepends it. Ask "where did we leave off?".
- Ask an agent in another project to do something without loading that
project's context here: cross-project messaging — "send project-b a request to
add the export endpoint" (
memory_message_send), and over there "check my inbox" (memory_message_pop). Seeagent-messaging.md.
Recipe: several accounts or an external launcher
Give each account or provider its own config home and pass it with --env, so
the harness, its hooks and MCP, and transcript import all use that one home:
ai-memory run --env CLAUDE_CONFIG_DIR="$HOME/.claude-work" claude
ai-memory run --env CODEX_HOME="$HOME/.codex-work" codex # the dir must exist
--envand--env-filebelong toai-memory run, so they go before the harness name.- A launcher or orchestrator that starts harnesses with its own environment
(provider keys, an account's config dir) can write it to a file and pass
--env-file <path>, oneKEY=VALUEper line. Values are taken literally, so use absolute paths in the file. - Auto-wire runs once per config home, so the second account gets its hooks +
MCP on its own first launch. To wire one by hand, export the variable for the
installers:
CLAUDE_CONFIG_DIR="$HOME/.claude-work" ai-memory install-hooks --agent claude-code --apply, then the same forinstall-mcp --client claude-code --apply.
Recipe: run an unsupervised agent safely (--yolo + ai-jail)
--yolo maps to each harness's dangerous-mode flag (Claude Code →
--dangerously-skip-permissions), which runs every tool call with no
confirmation. ai-memory run --yolo adds guardrails around that, gated
entirely on a real interactive terminal (stdin and stderr both TTYs), so
scripts, hooks, and CI are never prompted:
ai-memory run --yolo claude
- The warning. Before the agent spawns, you are asked to confirm:
Enter/y/yesproceeds (the default);n/noaborts before anything launches. - The ai-jail offer. If ai-jail
is usable — on Linux/macOS, installed on
PATH(or~/.local/bin/ai-jail), with its sandbox backend present (bwrapon Linux,sandbox-execon macOS) — and you are not already inside it, a second question offers to re-run the session inside it. When it is not usable (or on Windows) there is no second question; the run just proceeds. Accepting re-execs the original command underai-jail --network --agent-state --env <NAME>... --, forwarding only the credential/config environment variables that are already set (server/hook URL,CLAUDE_CONFIG_DIR, provider API keys, etc.) —--networkkeeps the loopback ai-memory server reachable while still sandboxing the filesystem. Declining keeps the run unsandboxed (your choice, already warned). - Already inside ai-jail. Both prompts are skipped and the run proceeds
directly —
ai-jail ai-memory run … --yolosees no extra friction. Detection is Linux (ai-sandboxhostname) / macOS (PS1starting with(jail)); it fails open (shows the warning) when undetectable, never open to skipping it silently. - Claude "true yolo".
--true-yoloincludes everything--yolodoes (ai-memory run claude --true-yolois enough; adding--yolotoo is harmless) and, for Claude, also forcesbypassPermissionsover anydefaultModein your settings. For other harnesses it is the same as--yolo.claude_true_yolo = trueinconfig.toml/AI_MEMORY_CLAUDE_TRUE_YOLO=trueapplies the Claude extra to every--yololaunch, never to a run without it. It cannot remove your ownaskrules: Claude Code honors explicitpermissions.askrules (and its built-in command-safety checks) in every mode, so a rule likeBash(docker run *)in~/.claude/settings.jsonstill pauses the run. For a pause-free sandbox, drop thoseaskentries —denyrules block without pausing, so they can stay. Best paired with ai-jail. - Passing extra env, e.g. a GitHub token.
ai-memory run claude --yolo --env GH_TOKEN="$(gh auth token)"forwards it into the jailed agent (needs ai-jail 2.4.2 or later when you accept the jail offer). This hands a sandboxed agent your token, so only do it for work you'd trust it with; it is deliberately never forwarded automatically.
See design-yolo-safety-ai-jail.md for the
full contract.
Recipe: send different repositories to different servers
One machine, several organisations, each with its own ai-memory server. Register each server once under a name, with the directory it is allowed to serve, then let each repository's marker pick it:
ai-memory server add team-a --url https://memory-a.example.com --root ~/work/team-a --auth-token-stdin
ai-memory server add team-b --url https://memory-b.example.com --root ~/work/team-b --auth-token-stdin
# ~/work/team-b/.ai-memory.toml
workspace = "team-b"
server = "team-b"
Repositories without a server key keep using the server install-hooks
configured. The marker only ever names a profile; a profile that does not
resolve drops the event rather than sending it anywhere else. Details, the
fail-closed rules, and which integrations support it:
marker-file.md.
Recipe: run the server on a Mac
Use the menu bar app when you want one .app that starts the server and
opens the existing tools (web UI, ai-memory status, config, logs). It does
not replace those tools with a second dashboard.
./companions/ai-memory-macos/build.sh
open "companions/ai-memory-macos/dist/AI Memory.app"
Drag AI Memory.app to /Applications, then Install & Start Server
from the menu extra. Wire an agent with the bundled binary:
BIN="/Applications/AI Memory.app/Contents/Resources/runtime/ai-memory"
"$BIN" install-mcp --client claude-code --apply
"$BIN" install-hooks --agent claude-code --apply
Memory stays in ~/Library/Application Support/ai-memory. Replacing the
.app does not rewrite it. Full paths (tarball, source, Docker, launchd):
macos.md.
From the terminal (CLI)
Your agent runs most of these for you; run and continue are how you start it:
ai-memory run <harness> # launch a harness, hooks + MCP auto-wired
ai-memory continue # resume the newest managed checkout
ai-memory workstreams # list this checkout's managed workstreams
ai-memory status # counts, paths, health
ai-memory doctor # is every harness that ran here captured?
ai-memory backfill # import prior local history into an empty store
ai-memory write-page … # save a durable page
ai-memory handoffs # list open handoffs for the project
ai-memory message list # cross-project inbox
ai-memory server list # server profiles a marker can route to
ai-memory export-okf … # export the wiki as an OKF bundle
ai-memory serve # run the server
When it isn't doing what you expect
- Nothing is being remembered: hooks may not be installed.
ai-memory run <harness>installs its hooks + MCP on the first launch per harness, ai-memory version and config home, and again afterai-memory uninstall. If that harness already launched throughrunand its hooks went missing another way (removed by hand, a failed first wire), install them by hand:ai-memory install-hooks --agent <your-agent> --applyandai-memory install-mcp --client <client> --apply. Then checkai-memory status/ai-memory doctor. - Only some agents are being remembered: run
ai-memory doctor. It lists every harness that has local sessions in this project and whether the server captured them — so a harness you rotated in without installing its hook (a silent gap: it keeps its own local history while capturing nothing) shows up as a warning with the exactinstall-hookscommand to fix it. - I just installed hooks in a project I've worked in for a while: the first
time you open the project after installing, ai-memory imports your existing
local session history once (bounded, sanitized on the server, only into an
empty store) so it isn't starting from nothing. It runs automatically in the
background; you can trigger or preview it yourself with
ai-memory backfill(--dry-runto see what it would import), or turn it off withAI_MEMORY_BACKFILL_ON_START=false. - A search misses something you saved: confirm the scope — memory is per-project; a page saved in project A is not returned in project B unless it was written to the global scope or you query with an explicit scope.
- You want an LLM feature (consolidation, digests): set a provider
(
AI_MEMORY_LLM_PROVIDER=…); without one, capture + search still work.