| memory_searchA | Search stored memories. Default: do NOT call — reach for it only when the user references shared context you lack ("my project", "the script we wrote") or a request is ambiguous in a way stored preferences could resolve. When a hit shapes your reply, announce it ("Using your stored preference for…") — non-negotiable. (Full policy: the server instructions block.) Returns ranked hits with snippets. Per-hit fields the model should branch on: relevance (high/medium/low) + matched_leg (lexical/semantic/both) — relevance is how much of your query wording the hit literally contains, not how good it is; on a semantic leg it is low by construction, so read them together.
staleness_verdict (fresh / spot_check_recommended / spot_check_required) — rolled-up signal; != fresh, act on path_drift below.
match_terms — which query words actually hit.
path_drift_missing (int) + path_drift ({checked, missing, verified} when drift detected) — body-cited paths gone. claim_anchored_missing (added when non-empty) is the escalating subset: memory_update those, memory_verify the rest. Prose-scraped missing rides fresh — evidence, not a tier.
commit_drift_count (int, when applicable) — commits since last_verified_at on the memory's origin repo. Non-zero means the project moved even if calendar-fresh.
depends_on_resolved (when present) — bounded auto-pull of depends_on link targets (max 3 per hit, max 10 per call). Each entry: {id, scopes, summary, link_note}. Surfaces context the query wouldn't on its own; saves a memory_show round-trip. OMITTED when the hit has no depends_on links.
recent_negative_outcomes (when present) — list of recent ignored/contradicted events for this memory (max two, one per outcome). The user already rejected this; don't re-surface unless you have new reason. OMITTED when none.
Parameters: query: nouns a memory would contain (tool, file, error names) — vocabulary is the lever, not question phrasing. Weak hits: re-query, different nouns.
scopes (optional): filter to scope union.
max_results (default 5, cap 50).
expand_top=True: inline the full body of the top hit when it has high relevance or a decisive score lead over the runner-up — saves a memory_show round trip and surfaces the full path_drift + commit_drift detail.
auto_scope=True (default): filter to current repo+worktree; memories with no recorded origin always pass as global. Set False for explicit cross-project queries.
since_prior_session=False (default): when True, filter to memories whose updated is strictly after the prior session boundary (latest event from a different session_id in the log). The semantic is 'what has changed in the current session, since the last activity by other sessions' — i.e. this session's intra-session diff. A /loop iteration uses this to track what IT has written/updated; for what the prior iteration did, call episode_handoff instead. Returns empty when there's no prior session in the log; distinguish 'nothing new' (results=[]) from 'no baseline' by also calling memory_scope_overview and checking curation_pending_new_since_last_session is None.
mode (optional, default from config; package default hybrid): keyword, bm25, semantic (needs an embeddings extra), or hybrid (RRF of all three; picks the extra up automatically). With no extra installed every mode is lexical, so see query.
Outcome is recorded automatically via the use_token within ~2 turns; only call memory_record_use to override (ignored / contradicted / corrected). |
| memory_showA | Fetch a single memory's full content by id. Typically used after a memory_search snippet looks relevant. The response carries the same staleness signals as a search hit: verification.status ('never' / 'stale' / 'fresh') with an actionable recommendation when not fresh.
staleness_verdict (fresh / spot_check_recommended / spot_check_required) — rolled-up signal across calendar, path and commit drift.
path_drift (the full report; missing-on-disk paths listed).
commit_drift (when caller is inside the memory's origin repo) — status: 'clean' | 'drift' + commits_since_verify.
Forward links and reverse_links for navigation.
When the verdict isn't fresh, spot-check one claim before relying. memory_verify(id, …) if it holds; memory_update if drifted (content updates reset last_verified_at, so verify again after the fix). |
| memory_listA | List active memories. By default returns one-line summaries (IDs, scopes, summary, no body) — cheap triage. Pass with_bodies=True to inline full bodies in one call; useful for small stores where N round trips of list -> show -> show would be wasteful. Don't reach for with_bodies casually — it pulls every memory in scope into your context, which is the failure mode this project exists to avoid. Filter by scopes if you only care about a subset. |
| memory_scope_overviewA | Cheap session-start hint: per-scope counts, no bodies / ids / summaries. Call once at the start of a conversation; if total is 0, skip memory_search for the rest of the session unless explicitly asked. Returns {current_repo, current_cwd, auto_scope, scopes: {scope: count}, total, disabled_scopes, curation_pending, curation_pending_new_since_last_session, recently_removed_in_worktree, proposals_pending, pending_writes}. proposals_pending is the count of write-reflex proposals the Stop hook has captured awaiting review via memory_proposals (0 unless the opt-in [proposals] auto_propose is on). pending_writes is this session's staged writes awaiting memory_write_confirm/cancel — a dangling confirmation (silent 1h expiry). curation_pending is an integer-count rollup the model should branch on:
{stale, never_verified, drifted, cold, dead, silent_misses, unique_silent_miss_memories, cold_endorsement_memories, conflicts}
conflicts = contradiction pairs awaiting a memory_conflicts verdict. Any non-zero dead or drifted is a cue to suggest a curation pass when the conversation has time. Non-zero silent_misses / cold_endorsement_memories means the audit-turn telemetry has actionable backlog. silent_misses counts events; unique_silent_miss_memories counts the distinct memories those misses pointed at (dedup'd by top-hit id). Misses whose top-hit memory has been tombstoned are excluded from both counters. cold_endorsement_memories counts distinct memories (NOT turns) with retrieval_count >= N AND zero explicit applies — usually a sign the memory is over-surfaced or stale. recently_removed_in_worktree is the integer count of tombstones removed in the trailing 7 days; under auto_scope=True it's filtered to this worktree (tombstones with no recorded worktree are excluded), under auto_scope=False it covers every tombstone in the window. Non-zero is a 'where did X go?' signal — material was deliberately trimmed here recently; don't blindly re-suggest it.
curation_pending_new_since_last_session is the same shape, filtered to events emitted and memories created since the previous session ended (not memories that aged into a bucket; an older record aging into stale between sessions stays visible only in the absolute curation_pending view — note this is distinct from the separate drifted bucket, which tracks working-tree drift). Branch on it when deciding whether to prompt about curation — non-zero means new rot since the last session, vs. the absolute view which persists until resolved. null on the very first session — fall back to curation_pending.
Default-scoped to the caller's current repository; memories with no origin always pass as global. Set auto_scope=False for the cross-project view. Counts respect session-disabled scopes. |
| memory_writeA | Create a new memory. Call PROACTIVELY when something durable enters the conversation — aggressive writing is safe; the guardrails below catch bad writes. Trigger→category mapping: the server instructions block. Parameters: content: the memory body.
scopes: non-empty list. Avoid the catch-all 'general'; prefer narrow tags like tools, infrastructure, projects:<name>, learning-style.
category (default 'fact'): one of fact, user-inference, ambient.
fact: project / infra / reference / tooling. Commits immediately (unless require_write_confirmation).
user-inference: claims ABOUT THE USER. Always returns {status:'pending', pending_id} regardless of config — ask the user in plain language, then memory_write_confirm or memory_write_cancel. Misattribution sticks; user gets the veto.
ambient: atmospheric context that shapes replies without being cited. Commits like fact but excluded from dead-weight curation; long bodies (>500 words) attach a non-blocking ambient_body_long warning.
confidence ('low' / 'medium' / 'high'), source ('explicit-statement' / 'inferred').
claims (optional): claims the body makes about this repo — path, path::symbol, path::NAME=literal. Checked against the worktree NOW (false ⇒ refused); drift then watches the claimed bindings, not whole files. Declare when citing code.
groundedness_check=True + source_transcript: optional gate. Sentences with <30% token overlap to the transcript return {status:'ungrounded', claims:[…]}. Override via acknowledge_ungrounded=True when you have grounding sources outside the transcript (file reads, tool results). Off by default; opt in for a paper trail.
Return statuses: committed — write succeeded; payload carries the new id and related medium-overlap matches.
duplicate — content dedup fired; the matched memory is credited a corroboration (corroboration_recorded: true, once per session) — recurrence is evidence; the hint carries the remedy.
transient_warning / credential_warning / previously_removed / scope_mismatch — gate rejects. Each returns what matched and a hint carrying the remedy and its acknowledge_* / force=True override.
user_claim_warning — the body reads as a claim ABOUT THE USER but category isn't user-inference. Re-issue as that (the user gets the veto) or pass acknowledge_user_claim=True if the subject is someone else.
pending — category='user-inference' or require_write_confirmation. pending_reason distinguishes.
ungrounded — groundedness gate fired.
A committed or memory_write_confirm response may inline a one-shot per-session curation_hint block when dead_weight + drifted + cold_endorsement_memories pressure crosses the configured threshold. Shape: {pressure, threshold, counts: {dead_weight, drifted, cold_endorsement_memories}, message}. Passive notification — call memory_health for full buckets, memory_remove / memory_verify to resolve. |
| memory_write_confirmA | Commit a memory_write that returned status='pending'. Pass the pending_id from that response. Pending writes expire after 1 hour; the confirm call will tell you which case fired (expired vs. never-existed). Re-gated at commit: duplicate / previously_removed / credential_warning can return instead of committed when the store changed during the wait. The staged write survives (pending_retained: true, same pending_id) — memory_write_cancel or resolve the match. The original write's overrides carry over. |
| memory_write_cancelA | Drop a pending memory_write without committing. Pass the pending_id from the original write response. Pending writes expire after 1 hour; if the TTL elapsed (or the id never existed) the call returns existed=False. |
| memory_updateA | Body edits clear last_verified_at; scope-only edits preserve it. Bundling a scope rename with a body edit clears verification. Refine an existing memory in place. Preferred over memory_remove + memory_write when correcting a stored fact — preserves id, created, and source; bumps updated. Parameters (pass at least one): id: required.
content: new body. Replacing the body clears last_verified_at, the verified-* attestations, and claims (the prior verification was for prose that no longer exists; call memory_verify again after, re-declaring claims). A body that reads as a claim ABOUT THE USER returns user_claim_warning unless the record is already user-inference; pass acknowledge_user_claim=True if the subject is someone else. An edit that SHRINKS the body and leaves it ending mid-sentence returns truncation_warning; pass acknowledge_truncation=True when the cut is deliberate.
scopes / links: REPLACE semantics — pass the full new list, or [] to clear.
confidence: low / medium / high.
category: accepts fact and ambient. user-inference is REJECTED here — that category exists to gate WRITES through the pending-confirm flow; updates have no equivalent gate.
Returns status="stale" when another agent updated the memory first; the hint says to re-fetch and retry. Each links entry is {type, target_id (a ULID), note?}. The types: supersedes (prefer this over the target), contradicts (both cannot be true), extends (adds nuance to it), depends_on (only makes sense in its context). docs/api.md carries the rest. |
| memory_removeA | Tombstone a memory. The file is moved to .tombstones/ with a removal reason and the originating session id — never hard-deleted. Use when a stored fact is wrong or no longer relevant. Tombstones remain searchable via memory_list_tombstones and are surfaced as removed_matches on memory_write when a new body looks similar to a previously-removed fact, so the lesson encoded in the removal reason isn't lost. Use memory_restore(id) to undo an accidental removal. |
| memory_verifyA | Bump last_verified_at to now after spot-checking that a memory's claims still match reality (file paths exist, version still matches, configuration still what it says). Orthogonal to content edits: this tool does NOT bump updated; memory_update does NOT bump last_verified_at. A typo fix bumps updated only; a verify call bumps last_verified_at only. Idempotent — calling twice slides the timestamp forward. Parameters: id: memory id.
note (optional, ≤500 chars): what was checked, for the event log.
verified_paths (optional list of strings): the ONLY attestation the drift legs read — checked against the memory's own worktree, and the anchor narrowing commit drift. Prefer it when the memory cites paths. Paths absent here are REFUSED.
verified_commits / verified_versions (optional lists): audit trail only; nothing on the read path resolves them.
verified_absent_paths (optional): attest paths INTENTIONALLY absent here (remote host, other platform, not-the-location) — reported under expected_absent, not missing. Never for real drift.
claims (optional): memory_write's claim syntax; checked NOW, false ⇒ refused. Stored claims re-check on every verify — a false one blocks the stamp; memory_update first.
All five lists are REPLACE, not append — None preserves the prior attestation, [] clears it, a populated list supersedes it. Attest the full set each time.
After memory_update on a memory you later spot-check, verify again — memory_update clears last_verified_at because the prior verification was for prose that no longer exists. Returns status="stale" when another agent verified first; the hint says to re-fetch and re-attest. Also resolves an unresolved record_use(contradicted) flag in memory_health when the body still matches reality. |
| memory_record_useA | Override the auto-applied outcome. Default behavior: every memory_search hit settles as applied at turn end (excerpts when the reply used it, auto=true otherwise). The common case handles itself — only call this tool when the model needs to record one of the three actionable outcomes below. Outcome | When | What it does | ignored
| retrieved but off-topic | annotates later hits; under outcome_demotion, mild 30d demotion (applied/update/verify clears) | contradicted
| stored fact disagreed AND not yet fixed | raises the unresolved-contradiction flag until a later memory_update or memory_verify clears it; demotes 2x under the same flag | corrected
| drifted and you fixed it inline (memory_update and/or memory_verify already called) | audit-only; does NOT raise the flag, never demotes |
applied is also accepted explicitly (rarely needed — auto handles it; call only to force-commit early).
Parameters: memory_ids: list (1+).
outcome: see table above.
note (optional, ≤500 chars): free-form context.
claim_excerpts (optional): list parallel to memory_ids (same length, None slots OK) carrying the load-bearing phrase that shaped the response. ≤500 chars per excerpt. Pass None for 'no specific claim' — empty strings are rejected (they're ambiguous: missing claim vs. zero-length claim). Especially useful on contradicted / corrected so the audit log records WHICH claim was wrong, not just that the memory drifted. Surfaces back in recent_negative_outcomes on later search hits.
|
| memory_audit_turnA | Not for in-conversation use. This tool is dispatched by the client's end-of-turn Stop hook; the model should never call this directly. Silent-miss telemetry (full reference in docs/api.md). Runs the search probe memory_search would have run for user_message (assistant_response optional), then checks whether a memory_search, memory_show, or memory_list event fired in the same session within lookback_seconds (default 60). A high-relevance probe hit with no retrieval in that window is a miss. Auto-scopes to the caller's repo so the probe matches the model's view; honours session-disabled scopes. Returns a MissReport with verdict in {'miss', 'ok', 'no_signal'} plus the top probe hits. Side-effects: emits turn_audited always, plus search_miss when the verdict is miss. |
| memory_scope_disableA | Disable a scope for the rest of this session. Subsequent memory_search and memory_list calls will exclude memories tagged with this scope. Useful when the user says 'this is unrelated to project X'. Resets when the server restarts. |
| memory_scope_enableA | Re-enable a previously disabled scope for this session. |
| episode_writeA | Append a journal-shaped entry for the current session — for run-state, iteration takeaways, 'what we tried'. Episodes are NOT durable facts; the durability gate that rejects transient memory_write content (TRANSIENT_PHRASE_MARKERS) does not apply here. Stored at /episodes//.md with a default 30-day TTL. Use this for content memory_write would reject as transient: 'iteration N tried X, fell over at step 3' 'currently blocked on Y; next step is Z' 'this branch's release plan' (state that changes weekly)
Episodes are invisible to memory_search / memory_health / memory_list — they are a sibling tier, not a memory category. Surface them via episode_handoff at iteration entry or episode_search for cross-session lookup. Promote a takeaway to durable memory via episode_promote (routes through memory_write, durability gate fires as normal). Returns {status: 'committed', id, session_id, created, scopes, takeaway, swarm_id, pruned_sessions}. pruned_sessions lists any prior session directories that hit the 30-day TTL on this write (typically []). Parameters: body: free-form markdown. Required, non-empty. Capped by max_content_bytes (default 1 MB).
swarm_id (optional): cohort id for multi-agent swarm fan-in. When a coordinator fans out parallel sub-agents, each sub-agent passes the coordinator's session id here so the coordinator can later gather every sub-agent's takeaways via episode_search(swarm_id=…). The episode still lives under this writer's own session; swarm_id is a cross-cutting label. Distinct from episode_handoff's single-chain predecessor link.
takeaway (optional): one-sentence summary. Surfaced preferentially at episode_handoff; when None, handoff falls back to the first line of body. Capped by max_takeaway_bytes (default 4 KB) — the takeaway lives in YAML frontmatter (64 KB ceiling), so an over-cap takeaway would corrupt the file and the episode would vanish from every read surface despite returning committed.
scopes (optional): list of scope tags. Empty list is valid (handoff keys on session_id, not scope). Capped by max_scopes_per_write (default 64) — scopes serialise into the YAML frontmatter (64 KB ceiling), so a runaway scope list would corrupt the file and the episode would vanish from every read surface despite returning committed.
|
| episode_handoffA | Read the most-recent journal takeaways from a prior session in this worktree. Call this FIRST at a /loop iteration entry — it answers 'what did the last session conclude here?' without needing memory_search. Episodes are the sibling-to-memory primitive for journal-shaped writes (see episode_write). When prior_session_id is omitted the handler auto-resolves it — the most-recent event-log session_id other than this process's own — under two implicit filters that mirror the opt-out cascade memory_search / memory_list honor: caller-worktree strict equality (None matches only None, so sibling worktrees stay isolated) and the disabled_scopes cascade (a session whose only takeaways are scope-disabled is skipped; surviving episodes are scope-filtered). Pass it explicitly to override (e.g. a child agent's parent id). Returns a dict: prior_session_id: the resolved session id, or None when no prior session exists in the log.
episodes: list of {id, created, takeaway, body, scopes} dicts, oldest first, capped at max_episodes (default 5, cap 50); each surfaces the writer's takeaway plus the full body.
note (optional str): set ONLY when the immediately-prior worktree session left nothing visible; names the cause — floor-only, zero-episode, promoted-out, or all-scope-hidden. The last two DID journal, so a note never means 'wrote no journal'. episodes MAY be non-empty — the walk rewinds to an older takeaway.
For ad-hoc lookup of an older session's journal, prefer episode_search with an explicit parent_session_id. |
| episode_searchA | Cross-session lookup for journal-shaped entries (episodes). NOT ranked — episodes are chronological and the filter set (scope / since / session_id) is the discovery surface. For the loop-iteration-entry case prefer episode_handoff, which auto-resolves the prior session and caps the surface. Returns {id, session_id, created, takeaway, body, scopes, swarm_id} per row, oldest-first inside the most-recent-max_results window: over the cap it keeps the MOST-RECENT N, so 'what did I conclude lately?' reads the tail, not the head. session_id is present because this surface spans sessions (unlike episode_handoff); swarm_id (may be null) is the multi-agent cohort tag — pass a coordinator's session id to gather every sub-agent's takeaways in one read. WORKTREE SCOPING: by default (auto_scope=True) the bare discovery walk (no swarm_id / parent_session_id) drops episodes whose captured git worktree differs from yours. PERMISSIVE, not a boundary, and weaker than the strict equality episode_handoff applies — it passes an episode through when there is nothing to compare (none captured, or you outside any git checkout), when the recorded worktree is gone from disk, and when you are in a LINKED worktree of the checkout that wrote it, so under agent fan-out the primary checkout's episodes stay visible. An EXPLICIT swarm_id / parent_session_id / ids is never worktree-filtered: naming a cohort or session is deliberate cross-worktree intent. Parameters (full reference in docs/api.md): scopes (optional): keep only episodes whose scope list intersects this filter.
parent_session_id (optional): restrict to one session's directory. Composes with swarm_id to narrow a fan-in.
swarm_id (optional): fan-in filter — episodes tagged with this cohort id, across all sessions.
since (optional ISO-8601): created at-or-after this instant.
auto_scope (default True): worktree-scope the bare walk (see WORKTREE SCOPING). False sweeps every worktree sharing the root.
max_results (default 20, cap 200): surfaces the most-recent N.
ids (optional): only these episode ULIDs — explicit selector, never worktree-filtered; unknown ids are absent, not an error.
include_bodies (default True): False OMITS body — takeaway-only rows. Scan, then re-read one via ids.
|
| episode_promoteA | Promote a journal entry (episode) into a durable memory. Routes through memory_write — the durability gate, scope-mismatch detection, dedup, and user-inference confirmation flow all apply. Use this when an iteration's takeaway turns out to be a fact worth keeping across sessions, not just a run-state note. Loop/working state belongs in episodes; session close is when to promote the takeaways that hardened. On successful commit the source episode is deleted (its content has been distilled). On pending (user-inference category), the source episode is held for memory_write_confirm to delete — memory_write_cancel keeps the episode so you can retry. On any other non-committed status (duplicate, previously_removed, transient_warning, scope_mismatch, ungrounded) the source episode is left untouched so you can adjust and re-promote. Body default: when use_body=False (default), the durable memory's body is the episode's takeaway. Set use_body=True to use the full episode body. An episode with no takeaway requires use_body=True. Returns the memory_write response shape with one extra field: promoted_from_episode_id: str so the caller can correlate the promotion attempt back to its source episode regardless of outcome (committed / pending / duplicate / scope_mismatch / etc.). Parameters: episode_id: ULID of the source episode.
scopes: scopes for the durable memory. Required.
category (default 'fact'): memory category. user-inference still requires explicit user confirmation.
confidence (default 'medium'), source (default 'explicit-statement'): standard memory_write fields.
use_body=False: when True, use the episode's body instead of its takeaway.
|