mirror of
https://github.com/akitaonrails/ai-memory.git
synced 2026-10-02 03:24:46 +08:00
- Adds HandoffAcceptStatus enum ('claimed' | 'consumed_by_hook' | 'none_pending')
returned alongside 'handoff' in memory_handoff_accept (#988, split from #920).
- Adds ReaderPool::handoff_claimed_by_live_session to verify if the caller's
live session already received the handoff via SessionStart hook.
- Reports 'consumed_by_hook' only when the caller forwards its session id
(e.g., Claude Code with --session-aware or OpenCode 2) and matches the
scope, live session, and owner filters.
- Reports 'none_pending' when no handoff was pending or for static clients
where the hook claim cannot be confirmed for that exact session.
- Updates documentation, routing skill, ARCHITECTURE.md, security boundaries (row 4g),
and CHANGELOG.md.
576 lines
33 KiB
Markdown
576 lines
33 KiB
Markdown
# Day-to-day usage
|
|
|
|
This page covers what happens after ai-memory is installed: handoffs,
|
|
compaction recovery, proactive memory queries, the web UI, and the
|
|
managed routing snippet + Agent Skills package.
|
|
|
|
## Capture exclusions
|
|
|
|
To keep recognized file-tool events under private paths out of ai-memory before
|
|
they are spooled or sent, configure `[capture] ignore_paths` in the nearest
|
|
`.ai-memory.toml`. The canonical grammar, limitations, support matrix, refresh
|
|
requirements, and safe local `--check-capture` command are in
|
|
[the marker-file reference](marker-file.md#capture-exclusions). Shell commands
|
|
are matched by their path arguments too (`cat docs/adr/*.md`), lexically. This
|
|
is not a general prompt/output DLP filter.
|
|
|
|
## Cross-agent handoff
|
|
|
|
You normally do not create handoffs by hand. With lifecycle hooks
|
|
installed, session-end capture writes the handoff and the next
|
|
session-start hook fetches it. Manual handoffs are project-wide and take
|
|
precedence over automatic SessionEnd handoffs. Among automatic handoffs that
|
|
match the receiving directory by path boundary, the newest is delivered;
|
|
creating a new automatic handoff expires prior open automatic handoffs from
|
|
that exact directory, and acceptance expires older matching automatic
|
|
handoffs without disturbing manual handoffs or pending work from sibling
|
|
directories.
|
|
|
|
```text
|
|
$ claude
|
|
> Working on the auth refactor. JWT rotation is broken; trying session cookies.
|
|
[work for an hour]
|
|
> /exit
|
|
|
|
$ codex # in the same directory, later
|
|
[SessionStart hook fetches the handoff; Codex sees it before your prompt.]
|
|
> Picking up: you were investigating session cookies as an alternative...
|
|
```
|
|
|
|
If an agent has MCP but no lifecycle hook surface, ask it to call
|
|
`memory_handoff_begin` before quitting. The next hooked agent can still
|
|
consume that handoff automatically. Grok shows it as `PostToolUse`
|
|
`additionalContext` after the first tool. Until that tool runs, or if the
|
|
session never calls one, call `memory_handoff_list` then
|
|
`memory_handoff_accept` with the listed `handoff_id`. Zero should do that
|
|
on resume. Listing does not claim the row.
|
|
|
|
`memory_handoff_accept` says why it returned no handoff. Its `status` is
|
|
`claimed` when the call took one, `consumed_by_hook` when the calling
|
|
session's own SessionStart already did (the handoff is in that session's
|
|
context), and `none_pending` when nothing is left to claim. Only a client that
|
|
forwards its session id on MCP calls can be told `consumed_by_hook`: Claude
|
|
Code through `install-mcp --session-aware`, or OpenCode 2. Any other client
|
|
gets `none_pending` after the hook consumed the handoff, so its agent still
|
|
checks its context for the delivered block first.
|
|
|
|
On a server that distinguishes operators, handoffs belong to their creator by
|
|
default: the next session for that operator sees their own plus deliberately
|
|
shared rows, never a teammate's. Use `shared: true` on
|
|
`memory_handoff_begin` only when the baton is intended for anyone in the
|
|
project. Root-authorized recovery can pass `any_owner: true` to
|
|
`memory_handoff_accept` or `memory_handoff_cancel`; normal callers cannot use
|
|
that switch.
|
|
|
|
Handoffs are next-session transfer, not a live message bus between agents that
|
|
are still running. In particular, Antigravity CLI exposes `PreInvocation`
|
|
before every model call; ai-memory fetches a handoff only on invocation zero,
|
|
which is the hook contract's startup boundary. A handoff created later in that
|
|
conversation stays open instead of being consumed by its creator's next model
|
|
call.
|
|
|
|
If an agent creates a handoff by mistake, cancel it immediately with
|
|
`memory_handoff_cancel` and the `handoff_id` returned by
|
|
`memory_handoff_begin`. Cancelling marks the handoff expired, so the next
|
|
session-start hook will not consume stale context.
|
|
|
|
## Compaction recovery
|
|
|
|
When Claude Code or Codex compact their working context, the
|
|
`PreCompact` hook fires and ai-memory writes a fresh
|
|
`sessions/<id>.md` page summarising the session so far. After
|
|
compaction, the agent can recover the summary via `memory_recent` even
|
|
though its raw chat history was compacted away.
|
|
|
|
Generated session pages carry `session_id` and `agent` in frontmatter. The
|
|
`agent` value is the originating harness stored on the session (for example,
|
|
`claude-code` or `codex`), not the client or operator that later requested a
|
|
consolidation. Checkpoints and superseding versions therefore keep the same
|
|
origin. Manual `memory_write_page` and `ai-memory write-page` calls do not
|
|
infer an agent.
|
|
|
|
## Proactive memory queries
|
|
|
|
Hooks handle capture without prompting. Proactive querying depends on
|
|
the agent knowing which MCP tool to call for each situation. Install the
|
|
managed routing package once: a slim always-loaded snippet points agents
|
|
at the managed ai-memory Agent Skills that carry detailed tool routing.
|
|
|
|
| You say | Agent calls | Effect |
|
|
|---|---|---|
|
|
| "Have we discussed X?" / "search memory for Y" | `memory_query` | FTS5 + entity/graph/vector RRF over compiled wiki pages, followed by bounded source-authority ranking and raw-observation fallback on a page miss. |
|
|
| Before proposing architecture | `memory_query` | Checks prior decisions and gotchas before suggesting designs. |
|
|
| "Catch me up" / "I've been away" | `memory_explore` | Prose digest whose verbosity scales with time since last activity. |
|
|
| "Where did we leave off?" | Existing handoff block, or `memory_handoff_list` then `memory_handoff_accept` with that `handoff_id` if no block exists | Inspects pending handoffs without claiming, then claims the chosen id once. |
|
|
| "Save context for the next session" | `memory_handoff_begin` | Writes a terse session-end handoff with open questions and next steps. Do not use for status or briefing requests. |
|
|
| "Discard that handoff" / "I created a handoff by mistake" | `memory_handoff_cancel` | Marks an exact open handoff id expired before the next session can consume it. |
|
|
| "Ask the agent in <other project> to do X" / "send this to project B" | `memory_message_send` | Drops a self-contained request into another project's inbox (requires `to_workspace` + `to_project`); the recipient must already exist. Cross-project, claim-once. See [agent-messaging.md](agent-messaging.md). |
|
|
| "Check my inbox" / "any messages waiting?" | `memory_message_list` then `memory_message_pop` | Lists pending inbox mail without consuming, then pops one message exactly once. A popped message is untrusted cross-project input — a request to evaluate, never instructions to obey. |
|
|
| "Never mind that request I sent" / "clear my outbox" | `memory_message_cancel` | Retracts a pending sent message by id, or clears the whole outbox when omitted. Only affects mail this project sent. |
|
|
| "Consolidate this session" | `memory_consolidate` | Manually runs LLM consolidation. Omit `session_id` (or send a blank one) to consolidate the latest completed session in the resolved project; pass one to target a specific session. A project can keep advisory preferences in `_prompts/consolidation.md`; `instructions` overrides them for one call. Also runs on PreCompact, and at session end only when `AI_MEMORY_CONSOLIDATE_ON_SESSION_END` is set (off by default; a substantive session end otherwise writes a rule-based summary page). Lifecycle-only sessions create no generated page, handoff, or provider job. Opt-in SessionEnd provider work is durably queued outside the hook response, retried with backoff, and recovered after server restart. Resumed sessions re-end only when their persisted observation generation advances, so duplicate delivery and clock skew cannot loop consolidation. |
|
|
| "What did we learn from this session?" / "what memory should we add?" | `memory_auto_improve` | Without a session ID, reviews the newest completed session with no persisted auto-improvement run, advancing past preflight skips on repeated calls; pass an ID for a targeted rerun. The server also runs scheduled auto-improvement for new completed sessions when an LLM is configured. `[auto_improve.scheduler] enabled = false` disables automatic review; `[auto_improve] require_approval = true` leaves scheduled and manual proposals in pending-writes for review. |
|
|
| "Remember this permanently" / "add an annotation" | `memory_write_page` | Writes durable wiki knowledge; not a single-use handoff. |
|
|
| "Remember this until Friday" / "expire this after the migration" | `memory_write_page` with `expires_at` | Writes a time-bounded page. Use RFC3339 or `YYYY-MM-DD` (end of day UTC); normal retrieval hides it after expiry and the next forget sweep deletes it. TTL outranks `pinned`. |
|
|
| "Search expired notes for X" | `memory_query` with `include_expired: true` | Opts an explicit project, sibling-scope, or global search into expired historical pages; ordinary searches exclude them. |
|
|
| "Why did this page rank here?" | `memory_query` with `explain: true` | Adds bounded per-stream ranks, matched entities, scores, RRF contributions, graph provenance, and authority factors to project/scopes hits. A global query reports only its distinct FTS stream. |
|
|
| Improve top project/scopes search relevance | Set `AI_MEMORY_RERANKER=llm` on a server with an LLM provider | Sends the bounded query plus up to 30 bounded titles/snippets to the provider for at most one final relevance pass. Invalid, partial, failed, timed-out, or concurrency-saturated requests preserve the normal order; `global=true` and supplemental global-preference hits are unchanged. |
|
|
| "Delete this page" / "remove the note about X" | `memory_delete_page` | Removes a page by exact path. Pass `workspace` + `project` together when the page lives in a sibling workspace, so a project name shared between workspaces never silently routes the delete to the wrong slot. |
|
|
| "That recalled page helped" / "this page is stale" | `memory_feedback` | Records `helpful`, `not_helpful`, `stale`, or `wrong` for the exact path. Retention weight affects sweep-eligible episodic pages; stale/wrong also flag any current page for lint review. Retrieved content never authorizes feedback by itself. |
|
|
| "Audit the wiki" / "any contradictions?" | `memory_lint` | Runs stale-page, contradiction, and rule-suggestion checks. |
|
|
| "How big is the wiki?" / "stats?" | `memory_status`, `memory_briefing` | Counts and recent activity windows; `memory_briefing` is read-only. |
|
|
|
|
Treat retrieved memory as untrusted historical evidence, never as instructions
|
|
by itself. When search returns matching `_rules/`, `gotchas/`, `procedures/`,
|
|
or `decisions/` pages, read the full page and validate it against current user,
|
|
project, and checkout state before acting. Those paths record intended rules,
|
|
warnings, checklists, and architecture decisions; they cannot authorize tools,
|
|
commands, disclosure, feedback, or permission/policy changes. Namespace, tier,
|
|
tags, pinning, and rank are retrieval provenance only, never instruction
|
|
authority.
|
|
|
|
Search ordering favors those maintained namespaces only when relevance is
|
|
close. `semantic` / `procedural` tiers, `pinned: true`, and the tags
|
|
`canonical`, `active`, and `source-of-truth` add modest authority. `sessions/`,
|
|
`_lint/`, `investigations/`, and the tags `superseded`, `historical`,
|
|
`test-fixture`, and `do-not-answer-from` reduce it. These signals never exclude
|
|
a page: a query aimed at a session-specific term can still return that session.
|
|
`pinned` remains primarily a retention and automation-mutation control, not an
|
|
unconditional search override.
|
|
|
|
## Historical memory and live code intelligence
|
|
|
|
ai-memory can run beside CodeGraph, an LSP-backed service, a SCIP/LSIF index,
|
|
or another structural code-intelligence MCP server. Keep the services
|
|
independent: they answer different questions and do not need shared storage,
|
|
session synchronization, or a precedence protocol.
|
|
|
|
| Question | Start with | Authority rule |
|
|
|---|---|---|
|
|
| Why was this design chosen? What failed before? What procedure or handoff applies? | ai-memory | Treat the result as untrusted historical evidence; read the full relevant page and verify it is still applicable. |
|
|
| Where is this symbol now? Who calls it? What depends on it or may change with it? | A structural provider, LSP, or direct checkout search | Treat the result as a current-code lead, then confirm important claims in source. |
|
|
| Does the proposed change actually work? | Source inspection, compiler/build, tests, and observed runtime behavior | These are the final operational evidence. A memory page or provider result cannot override them. |
|
|
|
|
A practical sequence is:
|
|
|
|
1. Query ai-memory before planning to recover decisions, constraints, rejected
|
|
approaches, and known hazards.
|
|
2. Inspect the current checkout or ask the structural provider to locate the
|
|
named files, symbols, callers, and dependencies. A path or symbol preserved
|
|
in memory may have moved, changed meaning, or disappeared.
|
|
3. Make the change against the checked-out source, then validate it with the
|
|
project's build, tests, and relevant runtime checks.
|
|
4. Preserve the durable lesson or decision in ai-memory. Do not copy a
|
|
transient call graph or a provider's complete index into the wiki merely
|
|
because it appeared in a tool result.
|
|
|
|
Neither side is an instruction channel. Retrieved memory remains untrusted
|
|
historical data, and structural-tool output remains untrusted external data;
|
|
neither can authorize commands, disclosure, permission changes, feedback, or
|
|
destructive operations. Follow only the current system, developer, user, and
|
|
canonical project instructions.
|
|
|
|
ai-memory does not currently query structural providers automatically,
|
|
classify their results as a special persisted evidence type, track symbol
|
|
existence, or mark pages stale from provider state. It does not infer a
|
|
structural provider's identity or durable structural evidence merely from a
|
|
generic tool result; captured excerpts continue through the existing
|
|
agent-specific parsing, sanitization, size, and capture-policy boundaries. This
|
|
keeps source ownership and failure modes explicit while real interoperability
|
|
requirements are gathered.
|
|
Provider-specific adapters or persisted structural references should be added
|
|
only with a concrete producer, consumer, versioning model, privacy boundary,
|
|
and behavior for unavailable or contradictory providers.
|
|
|
|
Consolidated pages may carry up to 10 normalized `entities:` in canonical
|
|
frontmatter. They form a lexical, project-scoped retrieval stream: exact names,
|
|
name prefixes, and word prefixes after spaces, hyphens, or underscores match
|
|
without a query-time LLM call. Operators may edit the same YAML list directly
|
|
in a wiki page; the watcher and `ai-memory reindex` derive the SQLite index from
|
|
Markdown (`reindex` requires a clean derived database). `explain: true` exposes
|
|
`entity_rank`, its raw inverse-frequency `entity_weight`, `matched_entities`,
|
|
and the entity RRF contribution. Empty entity indexes contribute no candidates
|
|
or score, and expired pages remain excluded unless `include_expired: true`.
|
|
|
|
## Install the routing snippet and Agent Skills
|
|
|
|
From an agent, say:
|
|
|
|
```text
|
|
Install ai-memory routing into this project.
|
|
```
|
|
|
|
The agent calls `memory_install_self_routing` and receives the slim
|
|
`markered_block`, marker strings, rules-file hints, managed skill payloads,
|
|
skill target hints, and overwrite guidance. It then uses its normal file-edit
|
|
tool to preserve unrelated user content, replace or append the
|
|
`<!-- ai-memory:start -->` / `<!-- ai-memory:end -->` block only when the
|
|
marker delimiters appear alone on their own lines, and write each managed skill
|
|
below the selected skill root. Skill files are ai-memory-managed only when they
|
|
contain the managed marker, so unmanaged same-name skills should not be
|
|
overwritten unless the human explicitly forces replacement.
|
|
|
|
The installed snippet and all project-scoped managed skills use the same
|
|
client-aware scope rule. A session-aware bridge that forwards the real hook
|
|
session id may omit scope for the current repository. A static MCP client must
|
|
send exact `workspace` and `project` values on every project-scoped call, even
|
|
when lifecycle hooks are installed, because hooks alone do not identify the MCP
|
|
request. Declare both names in the nearest [`.ai-memory.toml`](marker-file.md),
|
|
or obtain them from the operator/server configuration; never infer them from a
|
|
directory name or rely on the server's last active project. Global query and
|
|
global preference modes still omit project scope arguments.
|
|
|
|
From a terminal:
|
|
|
|
```bash
|
|
ai-memory install-instructions
|
|
ai-memory install-instructions --target AGENTS.md
|
|
ai-memory install-instructions --print
|
|
ai-memory install-instructions --no-skills
|
|
```
|
|
|
|
`install-instructions` installs or updates managed skills by default. Use
|
|
`--no-skills` only when you intentionally want a snippet-only refresh.
|
|
The CLI replaces only the markered ai-memory block, preserves unrelated content,
|
|
and writes a timestamped backup before changing an existing instruction file.
|
|
`install-instructions --print` previews the instruction snippet only; use
|
|
`install-skills --print` to preview skill payloads. Skill flags mirror
|
|
`install-skills` with an `--skills-` prefix:
|
|
`--skills-scope project|global`, `--skills-agent claude-code|agents|devin|grok|both`,
|
|
`--skills-target-dir <dir>`, and `--skills-force`.
|
|
|
|
Auto-detect extends `CLAUDE.md` when it exists, `AGENTS.md` when it
|
|
exists, both when both exist, or creates `CLAUDE.md` when neither exists. Use
|
|
`--target AGENTS.md` for non-Claude-only projects. The skill target follows the
|
|
instruction target unless you override it: `CLAUDE.md` implies
|
|
`.claude/skills`, `AGENTS.md` implies `.agents/skills`, and both files imply
|
|
both skill roots. For Grok Build CLI, select `--skills-agent grok` so skills
|
|
install under its `.grok/skills` root; for Hermes Agent, `--skills-agent hermes`
|
|
installs under `.hermes/skills` (project) or `~/.hermes/skills` (global).
|
|
|
|
When a project keeps `AGENTS.md` as its canonical instruction file, give it a
|
|
`CLAUDE.md` whose first line is a bare `@AGENTS.md` import. Claude Code loads
|
|
`CLAUDE.md` and does not read `AGENTS.md`, so without that import a block
|
|
installed with `--target AGENTS.md`, along with every project rule in the same
|
|
file, is absent from context at session start. A prose "read AGENTS.md" pointer
|
|
does not load the file; it asks the agent to open it, which leaves adherence to
|
|
whether the agent does. See
|
|
[Claude Code memory](https://code.claude.com/docs/en/memory#agents-md).
|
|
|
|
To refresh only the managed Agent Skills:
|
|
|
|
```bash
|
|
ai-memory install-skills
|
|
ai-memory install-skills --scope global --agent agents
|
|
ai-memory install-skills --scope global --agent devin
|
|
ai-memory install-skills --scope global --agent grok
|
|
ai-memory install-skills --scope global --agent hermes
|
|
ai-memory install-skills --agent both --print
|
|
ai-memory install-skills --target-dir .custom/skills --force
|
|
```
|
|
|
|
For Devin, project-local skills are installed under `.devin/skills`. Global
|
|
Devin installs use `%APPDATA%\devin\skills` on Windows and `~/.devin/skills`
|
|
on non-Windows systems. For Grok Build CLI, project-local skills go under
|
|
`.grok/skills` and global under `$GROK_HOME/skills` (default
|
|
`~/.grok/skills`).
|
|
|
|
Project-local skill roots are `.claude/skills` for Claude-compatible installs,
|
|
`.agents/skills` for cross-client installs, `.devin/skills` for Devin, and
|
|
`.grok/skills` for Grok. Global Claude/Agents roots are `~/.claude/skills` and
|
|
`~/.agents/skills`; global Devin roots are platform-specific as described
|
|
above; global Grok is `$GROK_HOME/skills` (default `~/.grok/skills`).
|
|
`--target-dir` points at an explicit skill root and bypasses scope/agent
|
|
inference. `--print` previews target paths and `SKILL.md` contents. `--force`
|
|
allows replacement of unmanaged same-name skills; without it, user-authored
|
|
skills are preserved. Uninstall removes ai-memory-managed skills from the
|
|
default project/global roots after marker validation; custom `--target-dir`
|
|
roots are a manual cleanup path.
|
|
|
|
This is prompt packaging only. ai-memory does not run a runtime skill router,
|
|
does not store durable memory in `SKILL.md`, and does not turn the
|
|
auto-improvement loop into a skill-authoring system. Durable knowledge still
|
|
lives in the wiki.
|
|
|
|
## Bootstrap an existing project
|
|
|
|
If you install ai-memory into a project that already has months of
|
|
history, the wiki starts empty. `ai-memory bootstrap` seeds it from the
|
|
existing repo history and docs.
|
|
|
|
```bash
|
|
export AI_MEMORY_SERVER_URL="http://localhost:49374"
|
|
ai-memory bootstrap --dry-run
|
|
ai-memory bootstrap
|
|
```
|
|
|
|
The bootstrap collector reads `git log`, the root README, `docs/`,
|
|
project rule files, and Rust module docs, then POSTs the selected
|
|
sources to the running server. It requires an LLM provider on the
|
|
server. See [Installation cookbook - bootstrap mid-project](install.md#bootstrap-mid-project)
|
|
for flags, token budgets, and source priority.
|
|
|
|
## Migrate from another memory tool
|
|
|
|
When replacing an existing memory system, treat the old data as untrusted
|
|
historical input until you curate it. Do not pipe raw transcripts or old memory
|
|
stores directly into ai-memory.
|
|
|
|
Migration checklist:
|
|
|
|
1. Export the old memory or history before changing hooks.
|
|
2. Keep the raw export as an archive, not as current project truth.
|
|
3. Scrub secrets, tokens, credentials, API keys, and raw logs that should not
|
|
become durable memory.
|
|
4. Curate the useful material into reviewed Markdown pages under a temporary
|
|
docs directory or directly into `concepts/`, `decisions/`, `gotchas/`,
|
|
`procedures/`, `notes/`, or `_rules/`.
|
|
5. If this checkout might be ambiguous, add `.ai-memory.toml` to pin the intended
|
|
workspace/project before importing or installing hooks.
|
|
6. Start `ai-memory serve` locally and confirm `ai-memory status` can reach the
|
|
server before touching existing client configs.
|
|
7. Import curated material first; avoid importing the full legacy raw history.
|
|
8. Verify expected pages with full hybrid `memory_query`; use
|
|
`ai-memory search` only when a terminal FTS5 lookup is sufficient.
|
|
9. Configure MCP and lifecycle hooks for one client at a time.
|
|
10. Only after ai-memory capture and retrieval work, disable the old memory
|
|
hooks, plugins, or MCP servers.
|
|
11. Search each client config for stale references to the old tool and remove
|
|
stale `Authorization` headers or env vars if bearer auth changed.
|
|
12. Restart each agent CLI after changing hooks, plugins, or MCP config.
|
|
|
|
Client cleanup hints:
|
|
|
|
- Claude Code: check plugins, hooks, old SessionStart injection, and MCP servers.
|
|
- Codex: check MCP config plus session/user-prompt/tool/compaction/stop hooks.
|
|
- Command Code: check `~/.commandcode/mcp.json` and the four stable lifecycle
|
|
events in `~/.commandcode/settings.json`.
|
|
- Devin CLI: check `.devin/config.json`, `.devin/hooks.v1.json`, and
|
|
`.devin/skills` for stale MCP, hook, or routing-skill entries.
|
|
- Gemini CLI and Antigravity CLI: check `settings.json` or equivalent hook/MCP
|
|
config files.
|
|
- Kimi Code: check `~/.kimi-code/mcp.json` and the `[[hooks]]` entries in
|
|
`~/.kimi-code/config.toml` (both under `$KIMI_CODE_HOME` when set) for stale
|
|
MCP or hook entries.
|
|
- Kiro CLI: check the `hooks` objects inside `~/.kiro/agents/*.json` (v2),
|
|
`~/.kiro/hooks/ai-memory.json` (v3), and `~/.kiro/settings/mcp.json` (all
|
|
under `$KIRO_HOME` when set) for stale ai-memory entries.
|
|
- OpenCode, OpenClaw, and OMP: check MCP config and plugin/extension directories;
|
|
move old memory plugins to a disabled/quarantine directory before deleting.
|
|
For the OpenCode 2 beta the plugin file is `ai-memory-opencode2.ts` and the
|
|
MCP entry lives under `mcp.servers` in the same `opencode.json(c)`.
|
|
- VS Code Copilot, Claude Desktop, and Zed: these are MCP-only, so confirm
|
|
whether the old tool was providing capture hooks elsewhere. Zed's MCP
|
|
entries live under `context_servers` in its user `settings.json`.
|
|
|
|
If you want a visible startup reminder during the transition, keep it small. A
|
|
rules-file note such as “Active memory: ai-memory; legacy export is historical
|
|
reference only; use memory_query for retrieval” is safer than dumping large
|
|
legacy context into every session.
|
|
|
|
If you use the ChatGPT/Codex OAuth provider, sign in once before starting the
|
|
server with `AI_MEMORY_LLM_PROVIDER=openai-oauth`:
|
|
|
|
```bash
|
|
ai-memory auth login openai-oauth
|
|
ai-memory auth status
|
|
```
|
|
|
|
The login command stores only provider credentials in `<data_dir>/auth.json`.
|
|
It is separate from `AI_MEMORY_AUTH_TOKEN`, the machine-root Bearer used by
|
|
MCP, hooks, handoffs, workstreams, and machine calls to dual-auth APIs.
|
|
|
|
For GitHub Copilot, use the matching provider login before starting the server
|
|
with `AI_MEMORY_LLM_PROVIDER=copilot`:
|
|
|
|
```bash
|
|
ai-memory auth login copilot
|
|
ai-memory auth status
|
|
```
|
|
|
|
Copilot auth stores a GitHub user token, then the provider exchanges it for a
|
|
short-lived Copilot API token before each LLM call.
|
|
|
|
## Browse the wiki in a browser
|
|
|
|
Start the server with `--enable-web` and open
|
|
`http://<host>:49374/web`.
|
|
|
|
```bash
|
|
ai-memory serve --transport http --bind 127.0.0.1:49374 --enable-web
|
|
```
|
|
|
|
On macOS the [menu bar app](macos.md#scenario-d-menu-bar-app) already starts
|
|
the LaunchAgent with `--enable-web`; **Open Web UI** opens that same URL.
|
|
|
|
Docker compose users can add the flag to the service command:
|
|
|
|
```yaml
|
|
command: ["serve", "--transport", "http", "--bind", "0.0.0.0:49374", "--enable-web"]
|
|
```
|
|
|
|
The web UI is read-only: project list, per-project page tree,
|
|
breadcrumbs, rendered markdown, metadata, and FTS5 search. In rendered
|
|
pages, `[[wiki links]]` become clickable links to the target page —
|
|
`[[path]]`, `[[path|label]]`, `[[project:path]]`, and
|
|
`[[workspace/project:path]]` are all supported (resolved against the
|
|
current page's project unless the target carries its own scope).
|
|
`[[…]]` stays literal inside fenced code (` ``` ` and `~~~` close
|
|
only by their own glyph), inline `` `…` `` code, and 4-space-indented
|
|
code; external schemes inside the brackets (`http://`, `https://`,
|
|
`mailto:`, `data:`, `javascript:`, `vbscript:`, `tel:`, `file:`)
|
|
stay literal too.
|
|
|
|
With no authority configured on loopback, the built-in wiki remains
|
|
anonymous. For human-authenticated administration, serve the compiled admin SPA
|
|
with `--web-ui-dir`: it signs in through `/auth/login` and uses an HttpOnly web
|
|
session plus CSRF protection. Before human auth is active, deprecated GET-only
|
|
browser compatibility accepts the root bearer through HTTP Basic and an
|
|
HttpOnly `ai_memory_auth` cookie; it stops immediately after a human password or
|
|
completed bootstrap exists. Browser-stored Bearers remain unsupported.
|
|
`AI_MEMORY_AUTH_TOKEN` and `aim_` API keys remain machine-only credentials sent
|
|
as `Authorization: Bearer <token>` by MCP, hook, handoff, and workstream clients.
|
|
|
|
To host the web UI under a URL subpath behind a reverse proxy, the
|
|
`--base-path` / `--web-slug` flags do the work — see
|
|
[`docs/frontend-api.md`](frontend-api.md#6-custom-ui-hosting-and-base-paths)
|
|
for the flag semantics and
|
|
[`docs/https-via-proxy.md`](https-via-proxy.md#hosting-under-a-subpath)
|
|
for the proxy-side walk-through.
|
|
|
|

|
|
|
|

|
|
|
|
## Inspect the raw wiki
|
|
|
|
The wiki is plain markdown plus git history.
|
|
|
|
```bash
|
|
docker exec ai-memory ls /data/wiki/sessions/
|
|
docker exec ai-memory cat /data/wiki/sessions/<uuid>.md
|
|
|
|
# Open in Obsidian or any markdown viewer:
|
|
docker cp ai-memory:/data/wiki ./my-ai-memory-wiki
|
|
|
|
# Time-travel:
|
|
docker exec ai-memory git -C /data/wiki log --oneline
|
|
```
|
|
|
|
## Move a session to another project
|
|
|
|
A session captured under the wrong project (a `cd` into a scratch
|
|
directory, a subagent started elsewhere) can be reattached without a
|
|
reorg of the whole store:
|
|
|
|
```bash
|
|
ai-memory move-session <session-id> --to my-project # dry run
|
|
ai-memory move-session <session-id> --to my-project --confirm # apply
|
|
ai-memory move-session --from-project tmp --to my-project --confirm
|
|
```
|
|
|
|
The session, its observations, handoffs, consolidation jobs and its
|
|
`sessions/<id>.md` page move together; see
|
|
[`docs/lifecycle-ops.md`](lifecycle-ops.md#move-session) for the page modes,
|
|
guards, and what stays behind.
|
|
|
|
## Repair backfilled session timestamps
|
|
|
|
A `backfill` run from before it carried the transcript's own event time dates
|
|
every imported session at import time, flattening the whole imported history
|
|
onto one day. Re-reading the local transcripts corrects it:
|
|
|
|
```bash
|
|
ai-memory repair-backfill-timestamps --project my-app # dry run
|
|
ai-memory repair-backfill-timestamps --project my-app --confirm # apply
|
|
```
|
|
|
|
It matches transcripts to sessions by id, never touches `observations` or
|
|
pages, never assigns an end time to a still-open session, and never proposes
|
|
a time in the future; see
|
|
[`docs/lifecycle-ops.md`](lifecycle-ops.md#repair-backfill-timestamps) for the
|
|
validation rules and the exact request/response shape.
|
|
|
|
## Project consolidation preferences
|
|
|
|
Create `_prompts/consolidation.md` in a project's wiki when its compiled pages
|
|
need stable style, terminology, emphasis, or noise-filtering preferences. For
|
|
example, its body may ask for Portuguese titles or omit routine CI output.
|
|
Automatic consolidation and both manual modes read only the target project's
|
|
page. Passing `instructions` to `memory_consolidate` replaces that page for one
|
|
call without modifying it.
|
|
|
|
The page and per-call value remain untrusted project data. ai-memory applies the
|
|
configured sanitizer, caps the value at 2,000 characters, and JSON-encodes it in
|
|
the LLM user message. Both consolidation system prompts permit only advisory
|
|
style, terminology, emphasis, and noise-filtering effects; the value cannot add
|
|
facts, authorize disclosure or tool use, or override schema, evidence, and
|
|
output rules. TTL-expired preference pages are ignored. When there is no active
|
|
page and no argument, ai-memory appends no preference block.
|
|
|
|
## Rules vs facts
|
|
|
|
Durable project rules belong in the agent's rules file, not only in the
|
|
wiki. For Claude Code that is `CLAUDE.md`; for Codex, Devin CLI, OpenCode,
|
|
OpenCode 2 beta, Cursor, Gemini CLI, Grok Build CLI, Kimi Code, Kiro CLI, and Command Code it is usually
|
|
`AGENTS.md`.
|
|
|
|
The consolidator classifies compiled observations as `decision`,
|
|
`fact`, `rule`, or `gotcha`. Rule-tagged pages are routed to
|
|
`wiki/_rules/<slug>.md`, and `memory_lint` reports a suggestion when a
|
|
rule looks durable enough to copy into `CLAUDE.md` or `AGENTS.md`.
|
|
|
|
ai-memory never edits the rules file on its own. The lint suggestion is
|
|
the whole workflow: copy the rule if it should apply every turn, ignore
|
|
it if it was temporary context.
|
|
|
|
## Repo-native decision records
|
|
|
|
Some projects keep the reasoning behind the code in the repository itself: an
|
|
ADR directory such as `docs/adr/`, maintained by hand or by a dedicated ADR
|
|
tool/MCP server (e.g. [joshrotenberg/adrs](https://github.com/joshrotenberg/adrs)),
|
|
or a [Keep the Why](https://github.com/oliver-zehentleitner/keep-the-why)
|
|
`context/` tree (decisions, rejected alternatives, constraints, reviewed in
|
|
pull requests). Three facts frame how such a record and ai-memory interact:
|
|
|
|
1. **ai-memory never touches files in your repository.** Its wiki lives
|
|
in the server's data dir; the background jobs (consolidation,
|
|
curation, retention decay, auto-improvement) read and write wiki
|
|
pages only. A decision-record directory in the repo is categorically
|
|
outside ai-memory's write surface. Run both side by side without
|
|
ceremony: the repo owns the canonical record, ai-memory owns
|
|
cross-session recall.
|
|
|
|
2. **Keep the record directory out of capture.** An agent reading the
|
|
record is captured like any other file read, and consolidation compiles
|
|
what it saw into wiki pages — including a `decisions/` page that says
|
|
"active" long after the repo has superseded it, ranked first by
|
|
`memory_query` because it matches the topic. List the directory in the
|
|
marker's `[capture]` section so the copy is never made:
|
|
|
|
```toml
|
|
[capture]
|
|
ignore_paths = ["docs/adr/**"] # or ["context/**"] for Keep the Why
|
|
```
|
|
|
|
The repo owns that record; a compiled copy goes stale the moment the
|
|
repo moves. Details and bounds in [`docs/marker-file.md`](marker-file.md),
|
|
including what shell matching cannot see and how to exclude large tool
|
|
results an agent saves and re-reads from another path.
|
|
|
|
3. **Wiki pages marked `pinned: true` are immutable to automation.**
|
|
Retention decay and curation skip them, multi-page consolidation
|
|
skips any update whose path names one (with a warning in the server
|
|
log; `_slots/` keep their own state/invariant regime), and the
|
|
auto-improvement apply path hard-refuses to rewrite them (the proposal
|
|
is recorded as a conflict with the reason). Unpinning is the explicit
|
|
opt-out.
|
|
|
|
For a project without a repo-side record, decisions go *in* the wiki, and
|
|
the managed durable-pages Agent Skill teaches agents the recipe:
|
|
`decisions/<slug>.md`, ADR structure (Status / Context / Decision /
|
|
Consequences, including rejected alternatives), `pinned: true`, and
|
|
supersede-by-new-page instead of editing history. Ask an agent to "record this as an architectural
|
|
decision" and the skill does the rest; the structured shape also
|
|
retrieves noticeably better through `memory_query` than free-form
|
|
prose.
|