Skip to content

memory operations

The exact fields each memory.* operation takes and returns.

Every op is POST /v1/<op> over HTTP and the MCP tool loom_<op>. The identity fields (org, namespace, agent, user_id, session_id) are filled from the token and the MCP session when a transport can; for an HTTP call with no token on a single-user server, the install's org on every op but code.* and tracker.* (which keep their plane's), and on some ops its namespace and agent; see identities and namespaces. The operations index explains role, kind and the MCP all listing. Unknown input fields are served and named in ignored_args.

memory.agents_list

Distinct agent names that have ever written into (org, namespace). agent + user_id are forced sentinels — we aggregate ACROSS them to discover which agents exist.

POST /v1/memory.agents_list · MCP loom_memory_agents_list · role viewer · read

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str '-' The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
Output field Type Meaning
agents list[str] Distinct agent names with a live embedding row in the namespace, sorted.

memory.aggregate

Templated SQL aggregation over typed records in loom.memory.

POST /v1/memory.aggregate · MCP loom_memory_aggregate · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
agg_func Literal['count', 'sum', 'avg', 'min', 'max'] 'count' Aggregate to compute: count (rows), or sum / avg / min / max of agg_field.
agg_field str '*' Field within value JSON to aggregate.
group_by list[str] (computed) Up to 3 value JSON field names to group by; empty = one row over all matches.
filter dict[str, str] (computed) Equality filter on JSON-extracted string fields; the identity tuple is always ANDed in.
limit int 100 SQL also hardcodes a LIMIT 1000 floor
Output field Type Meaning
rows list[_AggregateRow] One row per group (a single row when group_by is empty).
sql_executed str the exact SQL run, for debugging without trace logs

memory.apply

One scope grammar for every write that can touch more than one row.

POST /v1/memory.apply · MCP loom_memory_apply · role writer · write · audited

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
target ApplyTarget required Which rows: exactly one of keys (up to 500), filter (predicates, ANDed) or search (a query, optional min_score).
action Literal['delete', 'expire', 'lock', 'label', 'demote'] required What to do to each row; every action but delete needs its block below.
reason str required Why (1..512 chars, and for expire not starting with blacklist.sweep:); stored as lock_reason / expire_reason where the action has such a column.
limit int \| None None Row cap for a filter or search target, where it is required; unused for keys.
dry_run bool True True (the default) reports would_apply rows without writing; false writes.
expire ApplyExpire \| None None For expire: ttl_seconds or until, and an optional on_expire.
lock ApplyLock \| None None For lock: mode (read_only / no_delete), optional until / ttl_seconds.
label ApplyLabel \| None None For label: the tags and the mode — add, replace or remove.
demote ApplyDemote \| None None For demote: confidence_delta or confidence_factor; the result is clamped to [floor, 1].
Output field Type Meaning
action str Echo of the request's action.
dry_run bool Echo of the request's dry_run.
target str keys | filter | search
matched int Rows the target resolved to; a key with no live row is not counted.
applied int rows written (0 under dry_run)
would_apply int rows that would be written (dry_run)
refused int locks, policy, preconditions
noop int Rows already in the asked state: nothing was written for them.
failed int Rows whose write failed; each row's detail is the error's kind or a Loom error's code and message, never a driver's text.
truncated bool more of the caller's rows may match than limit
rows list[ApplyRow] One entry per matched row: its identity, status, new version and detail.

memory.ask

Ask a question and get a grounded answer synthesized from the caller's recalled memories (or, with allow_synthesis=False, just the memories).

POST /v1/memory.ask · MCP loom_memory_ask · role viewer · read

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str '*' Read ALL agents in the accessible namespaces by default (membership is the read boundary; agent is authorship metadata), matching memory.search, and serve every bot's promoted rules.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
query str required The question to answer from the caller's memories (1 to 2000 chars).
memory_type Literal['semantic', 'procedural', 'preference', 'reflection', 'decision'] \| None None Same semantics as memory.search: None = facts (reflections excluded); "reflection" = ask over past runs' process lessons only.
tags list[str] (computed) The same tag predicate memory.search applies, on the candidate read this ask synthesizes from — hard OR-filter by default (hasAny(tags, …)), a rank boost of tag_boost under tags_soft.
tags_soft bool False True turns tags into a rank boost of tag_boost instead of a hard filter.
tag_boost float \| None None Forwarded only when set (None = memory.search's own default, 0.3).
top_k int 15 15: a recency-aware synthesis eval on an autonomous-agent recall workload scored k=15 (with dated, trust-most-recent synthesis) at 57% vs 50% at k=8 — the extra candidates give the synthesizer the fresh fact even when stale cross-session…
bodies Literal['full', 'excerpt', 'none', 'unit', 'source'] \| None None Body mode of the retrieval leg: None keeps the deployment default; "source" serves each fact hit its linked evidence passage (or an N-character capsule with source_span_chars) so the reader sees the turn or trace the fact came from.
body_budget_tokens int \| None None Token budget for whole bodies on the retrieval leg; None = the memory.search default.
source_span_chars int 0 bodies="source": 0 serves the whole linked passage, N an N-character window around the extracted span.
allow_synthesis bool True False = retrieval_only: return the memories and let the agent synthesize (parity with code.ask's synthesize=False).
reinforce bool True An evaluation read.
reinforcement_weight float \| None None Per-request override of the decayed-reinforcement rank weight (None → server default), forwarded to the retrieval legs.
max_answer_chars int 0 Cap the synthesized answer length. 0 = unbounded (default).
namespace_scope Literal['this', 'all_mine'] \| None None Pass-through search controls, defaulting exactly as memory.search does: None takes the deployment default memory_search_default_namespace_scope, so an unscoped ask reads the same namespaces an unscoped search does.
search_mode Literal['semantic', 'lexical', 'hybrid', 'bm25_hybrid', 'related', 'graph', 'rrf', 'temporal'] 'rrf' Retrieval fusion mode, as memory.search takes it; forwarded only when set on the request, otherwise the search's own default applies.
read_scope Literal['namespace', 'own_user', 'own_session'] 'namespace' Same semantics as memory.search.read_scope; binds to this op's user_id / session_id identity fields.
sensitivity_penalty float \| None None Forwarded to memory.search unchanged: None = deployment default demotion of sensitivity:*-tagged rows, zeroed on contact intent.
summary_cap int \| None None Digest rows admitted to the synthesis window; None = the deployment setting, 0 = unbounded.
rrf_adaptive_lexical bool \| None None Forwarded to memory.search only when pinned.
personalization Literal['full', 'none'] 'full' Same semantics as memory.search.personalization; the retrieval leg of an ask runs with it so the answer can be compared with and without priors.
as_of datetime \| None None As-of temporal anchor.
as_of_basis Literal['valid_time', 'knowledge_time'] 'valid_time' Which time axis as_of replays: "valid_time" (default) = state true as of the anchor; "knowledge_time" = only what loom had learned by the anchor (created_at cutoff, incremental-ingest knowledge-cutoff semantics).
include_superseded bool False Forwarded to the retrieval leg; the retired rows then stay in the synthesis window, each line labelled [superseded <date>; replaced by <key>], so the answer can say what a value was before and what replaced it.
precedents bool \| None None Precedent reuse: serve the earlier synthesized answer to this question+shape when the synthesis window is byte-identical (same keys, same content hashes) to the one it was written from; no LLM call.
llm_cache bool \| None None Exact-prompt LLM response cache: every synthesis-path call is read from loom.llm_response_cache by the sha256 of its full prompt before the provider is called.
depth Literal['auto', 'excerpt', 'raw'] 'auto' Reading depth. "excerpt" answers from fact excerpts only (the default behaviour). "raw" also opens the source turns behind the most relevant facts (one selection call, a bounded evidence read, one more synthesis) on every call. "auto"…
deep_select Literal['llm', 'top'] \| None None How the raw read chooses what to open: "llm" (one selection call over the pool) or "top" (the top-ranked memories, no LLM — a deterministic descent from the ranked facts to their source turns).
deep_first bool \| None None When the deep pass is known upfront, synthesize once with the raw turns (None/True) or run the excerpt synthesis beside the selector, then the deep one (False).
recount bool \| None None Server-side recount of a COUNT answer.
token_budget int \| None None Input budget for the synthesis window, in estimated tokens (at the prose rate).
personalize bool \| None None A recommendation-shaped question ("can you suggest…", "what should I…") is answered from the user's recorded preferences, tools and constraints first, then tailored.
window_rel_floor float \| None None LLM-free window cut: keep synthesis rows whose cosine is >= this fraction of the best row's (e.g. 0.75), never fewer than LOOM_MEMORY_ASK_RELEVANCE_GATE_MIN_KEEP rows.
decompose Literal['', 'off', 'auto', 'always'] '' Question decomposition: "" or "off" = none; "always" splits this question into 2-4 sub-questions (the cheap tier) and retrieves each; "auto" does so only on multi-hop or date-of-event cues.
world_knowledge Literal['', 'forbid', 'allow'] '' General-knowledge bridging: "" = the server setting (LOOM_MEMORY_ASK_WORLD_KNOWLEDGE, default forbid); "allow" lets the synthesizer combine a memory fact with public knowledge, marking the sentences that did (output grounding = "mixed")…
consistency Literal['', 'inline', 'deferred'] '' When the answer-vs-citation judge runs. "" = the server setting (LOOM_MEMORY_ASK_CONSISTENCY_MODE, default inline); "inline" awaits it and rides the verdict on the wire; "deferred" answers first — the wire fields stay empty and the verdict…
route_prior str '' The verdict prior for the cheap-vs-strong router. "" = the server setting (LOOM_MEMORY_ASK_SYNTH_ROUTE_PRIOR, default off); "off" forces the rule-only router; "measured" the shipped table; or a JSON object of route -> contradicted rate.
route_prior_max_cheap float \| None None Contradiction rate at or under which a strong-routed ask goes cheap; None = the deployment setting.
mid_model str '' The mid synthesis tier, pinned per request.
mid_routes str '' Which routes use the mid tier: "" = the setting, "off", "uncertain" or a comma list of route reason tokens.
default_answer_chars int \| None None The deployment default for the answer length, pinned per request.
digest_descent bool \| None None Search-side digest descent for this call (None = on, False = off).
presupposition_rule bool \| None None Presupposition check in the synthesizer (None = on; False turns it off).
set_window int \| None None Synthesis window for set questions (None = setting, 0 = off).
knowledge bool \| None None Also read the knowledge plane (None = setting).
recent_messages list[_RecentMessage] (computed) The turns the question sits in, supplied by the caller and never stored — rendered as a RECENT TURNS block above the evidence so "and what did she say about that?" resolves against the turn above it.
observation_date str '' The question's own date (ISO yyyy-mm-dd), as memory.search takes it: "two months ago" resolves against it, for retrieval's date anchors and for the synthesizer's note.
gh_items_fallback bool \| None None Answer from loom's GitHub-items index when retrieval abstains on a question naming a PR/issue number.
Output field Type Meaning
answer str The synthesized answer; empty when the ask abstained or ran retrieval_only.
source Literal['synthesized', 'retrieval_only', 'abstained', 'record', 'precedent'] "record": answered from an exact record (the GitHub-items index or the tracker) instead of LLM synthesis. "precedent": the earlier synthesized answer to this question+shape, served because its evidence is byte-identical.
abstain bool True when retrieval abstained and no record answered: answer is empty.
abstain_reason str Carries the retrieval abstain reason, or "budget_capped" when synthesis was skipped because the namespace is over its cost cap.
memory_keys list[str] Citations: the memory_keys the answer drew on (all recalled keys in retrieval_only mode).
results list[_MemorySearchHit] The raw retrieved memories, so callers still see the sources.
cited_sessions list[str] The sessions the cited memories were written under: the session_id of every hit in results whose key is in memory_keys, first-cited order, no repeats, "" skipped.
next str Protocol nudge, set only when the call carried a run_id (see MemorySearchOutput.next).
synth_model str Synthesis routing (two-tier cascade, LOOM_MEMORY_ASK_SYNTH_MODEL_CHEAP).
route_tier Literal['', 'cheap', 'mid', 'strong'] The synthesis tier that answered: cheap, mid or strong; empty when nothing was synthesized.
route_reason str The routing rule that chose route_tier ("single_tier" when the cascade is off); empty when nothing was synthesized.
llm_cache_hits int How many of this ask's LLM calls the exact-prompt response cache served (no provider call, nothing spent) and how many went to the provider.
llm_cache_misses int LLM calls this ask sent to the provider past the response cache.
llm_cache bool Whether the cache wrapped this ask at all — the request pin, the traffic class's policy or the deployment default, whichever applied — and the dollars the hits did not spend at the price table (the stored token counts of the served rows).
llm_cache_saved_usd float Dollars the cache hits did not spend, at the price table.
temporal_consistency float \| None What the answer cited, scored without a model call (cited_sessions above is the list these are computed on). temporal_consistency: mean window score of the cited rows' dates against the question's bounded window (None = no window / no…
citation_concentration float \| None 1 / distinct cited sessions; None when nothing was cited.
escalated bool True when the cheap-tier answer failed its check and was redone on a stronger tier.
gate_mode str Similarity-knee telemetry (window_rel_floor). gate_mode is "" when the cut was off or skipped, else knee | floor | kept_all; the counts say how many window rows the synthesizer actually read vs. dropped.
gate_kept int Window rows the synthesizer read after the similarity-knee cut.
gate_dropped int Window rows the similarity-knee cut removed.
pending_extraction int Deferred batches not yet extracted in scope; see MemorySearchOutput.pending_extraction.
pending_evidence int Raw turns of those pending batches that were read into the synthesis window because they spoke to the question. > 0 says the answer used evidence no memory row carries yet.
retrieval_query str The text retrieval actually embedded when the query carried an answer scaffold (option block, "reply with the letter", stamped date) that was split off for the synthesizer.
facets list[str] Facet completion: on a count question, the attribute facets pulled (age, location) and how many in-scope memories they added to the synthesis window beyond similarity retrieval.
facet_memories_added int In-scope memories the facet pull added to the window beyond similarity retrieval; 0 when it did not run.
consistency Literal['', 'entailed', 'neutral', 'contradicted'] Answer-vs-citation audit (LOOM_MEMORY_ASK_CONSISTENCY_CHECK): does the answer say what its cited memories say? "entailed" / "neutral" / "contradicted", "" when the check did not run. consistency_score is (1 + P_entail - P_contradict) / 2.…
consistency_score float (1 + P_entail - P_contradict) / 2, 0 to 1; 0 when the check did not run.
consistency_note str The cited memory and phrase a contradiction turns on; empty otherwise.
identifier_chase_hops int Identifier chase: hops walked from the question's identifiers through the identifiers the reached memories name, and the memories those hops added to the window. 0 / 0 when the question named no identifier or the reached memories named…
identifier_chase_added int Memories the identifier-chase hops added to the window.
premise_conflict str The question's premise checked against PINNED or human-VERIFIED memories in the window (LOOM_MEMORY_ASK_PREMISE_CHECK).
premise_note str One line naming the presupposition and what the trusted memory records; empty when no conflict was found.
premise_verdict str The synthesizer's premise verdict (supported / unsupported / "").
attribution_note str "fired" when the computed attribution note rode the synthesis call, "" otherwise.
depth Literal['', 'excerpt', 'raw'] Reading depth that produced the answer: "excerpt" = fact excerpts only, "raw" = the source turns behind the selected facts were opened too.
deep_fired bool True when the deep-evidence pass ran (see deep_reason), even if it opened no source text.
decomposed list[str] The sub-questions retrieved on their own; empty when the question was retrieved whole.
decomposed_parts int How many sub-questions that was (0 = retrieved whole).
superseded_dropped int Superseded rows taken out of the synthesis pool; 0 under include_superseded.
stale_sentences_masked int Sentences in chunks and summaries masked because they restate a superseded row.
entity_followed list[str] Bridge entities the second hop followed.
when_evidence_turns int Dated raw turns the second synthesis read (0 = no fallback).
decompose_failed int Sub-question retrievals that raised and were skipped (fail-open), and the widened pool the retrieval ran with (0 = the caller's top_k).
pool_k int The widened candidate pool retrieval ran with; 0 = the caller's top_k.
grounding Literal['', 'memory', 'mixed'] "memory": every sentence came from the memories; "mixed": the answer bridged with general knowledge under world_knowledge="allow" and marked those sentences; "": no synthesis ran.
deep_reason str Why the raw read ran: "requested" (depth="raw") or "temporal"; empty when it did not.
deep_keys list[str] The memory keys whose source turns the raw read opened.
deep_chars int Characters of source text the raw read handed the synthesizer.
code_hits int Code-plane grounding rows the synthesizer saw.
pool_facts int Fact rows and verbatim chunk rows the synthesizer saw.
pool_chunks int Verbatim chunk rows the synthesizer saw.
prompt_blocks dict[str, int] Estimated tokens per block of the synthesis prompt — system, notes, window (fact and summary rows), chunks, code, chase, knowledge, raw_turns, timeline, recent, scratchpad, elision, graph, question, other (heads and separators) — summed…
deep_selector Literal['', 'llm', 'top'] Which selector chose the opened memories: "llm" or "top" ("" when the raw read did not run).
deep_first Literal['', 'single', 'empty_block', 'deep_failed'] How the deep-first order resolved. "single" = one synthesis with the raw turns (no excerpt synthesis ran); "empty_block" = the raw block was empty so the excerpt synthesis ran alone; "deep_failed" = the deep answer failed the citation…
synth_calls int How many synthesis-family calls the ask made (primary, deep, escalation, fallbacks; 0 when nothing was synthesized).
synth_input_tokens int The provider-side accounting of the model that answered (synth_model) over this ask: its calls, whole-prompt input tokens, the share the provider served from its prompt cache and the share it wrote to it, and its dollars at the price…
synth_cached_input_tokens int Input tokens of the answering model the provider served from its prompt cache.
synth_cache_write_tokens int Input tokens of the answering model the provider wrote to its prompt cache.
synth_cost_usd float Dollars the answering model's calls cost at the price table, cache rates applied.
context_tokens_est int Estimated input tokens of the memories the synthesizer saw, and how many ranked memories token_budget cut from the window (0 when no budget).
context_trimmed int Ranked memories token_budget cut from the window; 0 when no budget.
summary_hits int Session-summary hits that survived into the synthesis window.
knowledge_hits int Knowledge passages the synthesizer was shown.
timeline_rows int Dated memories rendered in the computed timeline; 0 = none.
count_recomputed bool A COUNT answer whose number was rewritten from the model's own ITEMS verdicts.

memory.assign_category

Link a memory to one of the namespace's registered categories.

POST /v1/memory.assign_category · MCP loom_memory_assign_category · role writer · idempotent · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
memory_key str required The memory to categorise; the key is stored as given, not checked for a live row.
category_name str required A category registered in the namespace (namespace.register_category), else 404.
tombstone bool False True rescinds the memory's link to the category.
Output field Type Meaning
assigned bool True when the link was written; False when it was rescinded.
category_id str The id of the named category.

memory.auto_retirements

The memories Loom retired on its own (the conflict resolver, newest_wins), newest record first, inside the request's read scope.

POST /v1/memory.auto_retirements · MCP loom_memory_auto_retirements · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
since datetime \| None None Only records written at or after this time; unset, the last 30 days.
limit int 50 Retired memories per page, 1 to 200 (default 50).
cursor str \| None None The next_cursor of the previous page; unset, the newest page.
Output field Type Meaning
rows list[MemoryAutoRetirement] One row per retired memory, newest record first.
next_cursor str \| None Pass back as cursor for the next, older page; None when this page is the last.

What points at one memory: its version chain, the digests whose lineage names it, the typed links in and out of it, how many evidence turns support it, and the memories that share its entities.

POST /v1/memory.backlinks · MCP loom_memory_backlinks · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
memory_key str required The memory whose inbound references are read.
limit int 20 At most this many entries in each returned list.
Output field Type Meaning
memory_key str Echo of the requested key.
found bool True when the key has at least one stored version.
versions list[_BacklinkVersion] The key's versions, newest first, with status and write time.
digests list[_BacklinkDigest] Live session digests whose derived_from names this key, newest first.
links_in list[_BacklinkEdge] Live operator links pointing at this key: the source key and relation.
links_out list[_BacklinkEdge] Live operator links from this key: the target key and relation.
evidence_count int Distinct live evidence items (belief sources) that support this key.
related list[_BacklinkRelated] Other memories sharing an entity with this key, most shared first (up to four shared entities listed per memory).

memory.brief

A task or question to orient on.

POST /v1/memory.brief · MCP loom_memory_brief · role viewer · read

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
query str required The task or question to orient on, passed to memory.ask.
top_k int 15 How many rows memory.ask retrieves; also caps evidence.
tags list[str] (computed) Tag filter passed to memory.ask; empty = no tag filter.
Output field Type Meaning
query str Echo of the request's query.
answer str The answer memory.ask synthesised from the memories; '' when none.
abstained bool True when memory.ask abstained: the memories did not answer it.
grounding str "memory" = every sentence from the memories; "mixed" = an inferred / bridged sentence was marked; "" = no synthesis.
evidence list[BriefEvidence] The rows the answer cited (every retrieved row when it cited none), at most top_k.
unknowns list[str] What the memories did NOT cover: the abstain reason when the ask abstained.
next_reads list[str] Concrete next steps: the sessions the answer cited (open them), and searches for the unknowns.

memory.bulk_get

Fetch many memory entries by key in one CH round-trip.

POST /v1/memory.bulk_get · MCP loom_memory_bulk_get · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
memory_keys list[str] required Keys to read under this identity; a chunk:memory.set:<key>:<n> id from a search reads its parent fact when no row is stored under that key, and the parent answers under the id it was asked by.
fields list[str] (computed) Whitelist of output field names applied to every entry in results (each keeps found, and resolved_from when set).
Output field Type Meaning
results dict[str, MemoryGetOutput] Named results (not found) to avoid colliding with the per-row found: bool inside MemoryGetOutput.
missing list[str] Asked keys with no live row under this identity.

memory.bulk_set

Insert many memory entries in one batched CH insert.

POST /v1/memory.bulk_set · MCP loom_memory_bulk_set · role writer · idempotent · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
entries list[_BulkSetEntry] required The entries to write, each with its own key, value and per-entry fields; all share the caller's identity.
contextualize bool False Treat this call's entries as one document and prepend a model-written one-line context to what each entry EMBEDS and to its content_excerpt (value untouched).
detect_updates bool \| None None See MemorySetFromMessagesInput.detect_updates (same semantics).
Output field Type Meaning
written list[_BulkSetResult] One result per entry, in order: key, version (0 when not inserted) and verdict.
skipped_pinned list[str] Keys whose live row is pinned: skipped (result version=0) rather than overwritten, since overwriting would also unpin (entries default pinned=False).
skipped_locked list[str] Keys whose live row is locked read_only: skipped (result version=0), where memory.set would answer 423 LOCKED.
skipped_blacklisted list[str] Keys whose entry the blacklist refused or could not check in the request's budget: not written (result version=0), where memory.set would answer 422 BLACKLISTED.
merged list[str] Conflict verdicts, the same four memory.set produces: canonical restatement of an existing row → not inserted, existing row reinforced; close to a pinned peer → not inserted; same fact in other words → inserted as status=duplicate…
value_updates_linked int Older rows marked superseded by a value update in this batch.
rejected_pinned list[str] Keys not inserted because they are too close to a pinned row.
duplicates list[str] Keys inserted as status=duplicate: the same fact as a live row in other words.
conflicted list[str] Keys inserted and cross-linked as status=conflicted with the rows they contradict.
unchecked int Entries past bulk_conflict_check_max_entries, written without a check.
retyped list[str] Entries declared procedural whose value is not a procedure body, stored as semantic tagged retyped_from:procedural.
identity_duplicates dict[str, str] Entries not written because a live row in the same identity already carries the same content: entry key -> the keeper's key.
stage_ms dict[str, float] Wall time per phase of the write (existing_lookup, entity_resolution, prepare, embed, verdicts, insert, value_updates, projections, total) — Diagnostic; readers tolerate unknown keys.

memory.clear

Bulk-tombstone memories in one scope.

POST /v1/memory.clear · MCP loom_memory_clear · role admin · idempotent · audited

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
user_id str \| None None The user whose rows are cleared ('-' for rows with no user); '*' or unset clears every user's.
agent str \| None None The agent whose rows are cleared; '*' clears every agent's, and unset clears every agent's over HTTP but only the calling client's over MCP.
confirm_subject str required Must equal the concrete user_id or agent being cleared ('*' and '-' never confirm); anything else is a 400.
Output field Type Meaning
cleared_at datetime UTC time the clear ran.
rows_affected dict[str, int] {table_name: rows_tombstoned}
total int sum across all tables
locked_skipped int Live rows in scope that carried a lock in force and were left alone.

memory.consolidate

Periodic consolidation pass — merges fragmented memories about the same subject that slip past hash-dedup and extraction-time dedup.

POST /v1/memory.consolidate · MCP loom_memory_consolidate · role writer · write · audited

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
limit int 200 At most this many of the identity's newest eligible rows are clustered.
similarity_threshold float 0.85 Cosine at which two rows may cluster; they also need a shared entity unless the cosine reaches max(threshold + 0.05, 0.92).
min_age_seconds int 3600 Skip memories newer than this — protects in-flight writes from being consolidated away before retrieval can use them.
dry_run bool False True still runs the merge LLM calls but writes nothing: no canonical row, no tombstones.
Output field Type Meaning
consolidated list[MemoryConsolidateResult] One entry per cluster merged (or that would be, under dry_run).
examined int Eligible rows read: live, unpinned, unlocked, embedded, older than min_age_seconds, and neither labelled high nor reading as high (never sent to the merge model).
clusters_found int Clusters of two or more rows found.
clusters_failed int Clusters not merged: the merge LLM call failed or returned an unparseable or incomplete JSON object, the merge guard refused it, a merged row changed since the read, the summary's keys were taken, or the summary could not be embedded.
clusters_refused_lossy int Of the failed clusters, those the merge guard refused because the merged text lost a date, quantity, identifier, name or negation one source carried; the sources stay as they were.
skipped_in_flight int Always 0: rows newer than min_age_seconds are left out of the read, not counted.
tokens_in int Prompt tokens of the merge LLM calls.
tokens_out int Completion tokens of the merge LLM calls.

memory.copy_to

Self-service copy of a memory into another namespace.

POST /v1/memory.copy_to · MCP loom_memory_copy_to · role writer · write · audited · not listed under the MCP all toolset

Input field Type Default Meaning
from_org str required Org (source and target share it).
from_namespace str required Source namespace.
memory_key str required Source memory_key to copy.
from_agent str '*' Which agent's row to copy; '*' resolves to the most-recently-written row for the key (namespace-wide).
from_user_id str '-' The source row's user id, part of the identity the key is looked up under.
from_session_id str '-' The source row's session id, part of the identity the key is looked up under.
to_namespace str required Target namespace.
target_key str \| None None Destination memory_key.
target_agent str '-' Attribution in the target; '-' reads as namespace-owned.
rationale str '' Human-readable reason; the audit row keeps only its SHA-256 and length.
Output field Type Meaning
copied bool False = source row not found / tombstoned
target_memory_id str new UUID in the target namespace
target_namespace str The namespace the copy was written into, echoed.
target_key str The copy's memory_key: target_key, else the source row's key.

memory.cross_promote

Cross-namespace promotion with provenance.

POST /v1/memory.cross_promote · MCP loom_memory_cross_promote · role viewer · write · audited · not listed under the MCP all toolset

Input field Type Default Meaning
from_org str required Source namespace's org.
from_namespace str required The source row's namespace; the caller needs ADMIN on it, and the row must live there.
from_memory_id str required UUID of the source row.
to_namespace str required The namespace the copy is written into, in the same org; the caller needs ADMIN on it.
target_key str \| None None Override the destination memory_key.
target_agent str '-' Agent identity to attribute the target row to.
rationale str '' Human-readable reason; the audit row keeps only its SHA-256 and length.
Output field Type Meaning
promoted bool False = source row not found / tombstoned
target_memory_id str new UUID in the target namespace
target_namespace str The namespace the copy was written into, echoed.
target_key str The copy's memory_key: target_key, else the source row's key.
cross_promoted_from_memory_id str The source row's id, also stored on the copy as its lineage.

memory.dedup_collapse

Collapse every duplicate pair of a namespace onto its canonical — no LLM, every key kept, nothing tombstoned.

POST /v1/memory.dedup_collapse · MCP loom_memory_dedup_collapse · role writer · idempotent · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
prefix str '' Only duplicates whose memory_key starts with this ("" = the namespace).
dry_run bool True True only counts; False re-emits the stale mirror rows as duplicate.
limit int 500 Pairs examined per call, by key; truncated says a second call is due.
sample int 20 Pairs echoed back in sample.
Output field Type Meaning
namespace str The namespace the op reads or writes.
prefix str The key prefix the pass covered, echoed ("" = the whole namespace).
dry_run bool Whether this was a count-only pass, echoed.
pairs int Live duplicate rows naming a canonical (bounded by limit).
canonical_live int ... whose canonical is a live active row.
orphans int ... whose canonical is gone: memory.resolve_orphan_duplicates re-activates them.
mirror_stale int Mirror rows of live pairs still carrying a status other than duplicate.
restamped int Mirror rows re-emitted as duplicate (0 under dry_run).
truncated bool True when limit pairs were read, so more may remain: call again.
sample list[_DedupCollapsePair] Up to sample pairs whose canonical is live, as duplicate → canonical keys.

memory.dedup_list

List clusters of status='duplicate' rows in a namespace.

POST /v1/memory.dedup_list · MCP loom_memory_dedup_list · role admin · idempotent · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
limit int 50 At most this many clusters (1–200), largest first; each lists up to 50 duplicates.
Output field Type Meaning
clusters list[_DedupCluster] Live duplicate rows grouped by their canonical, ordered by duplicate count descending.

memory.dedup_resolve

Resolve one duplicate pair.

POST /v1/memory.dedup_resolve · MCP loom_memory_dedup_resolve · role admin · idempotent · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
canonical_id str required The cluster's canonical_id from memory.dedup_list; must be a live row here (else 404).
duplicate_id str required A duplicates[].memory_id of that cluster, marked duplicate of canonical_id (else 404).
decision Literal['keep_canonical', 'keep_duplicate', 'keep_both'] required Which side stays live; see the class docstring for each value.
confirm_canonical_key str required Must equal the canonical row's key, else 400 CONFIRM_CANONICAL_KEY_MISMATCH.
canonical_key str '' Removed, as a key does not name one row: accepted, unused and named in ignored_args.
duplicate_key str '' Removed, as a key does not name one row: accepted, unused and named in ignored_args.
Output field Type Meaning
canonical_key str The canonical row's key.
duplicate_key str The duplicate row's key.
decision str The decision applied, echoed.
canonical_tombstoned bool True when keep_duplicate tombstoned the canonical row.
duplicate_tombstoned bool True when keep_canonical tombstoned the duplicate row.
ignored_args list[str] Request fields the server did not apply: unknown keys and the removed key fields.

memory.delete

WHAT: Tombstone a memory by key (14-day recovery in the dashboard).

POST /v1/memory.delete · MCP loom_memory_delete · role writer · idempotent · audited

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
memory_key str required The key to delete: tried under this identity, then the same user's row under any agent.
Output field Type Meaning
tombstoned bool True when a live row was found and a tombstone version written over it.
version int Nanosecond version of the tombstone row; when nothing was found, a fresh number that no row carries.
found bool False when no live row of this key exists in the namespace under ANY identity — already deleted, or never written.
dependents_tombstoned int Consolidated summaries built from this row (tag src:<key>): retired when none of their sources survive, else downgraded to conflicted.
dependents_downgraded int Summaries built from this row that keep a live source: set to conflicted.

memory.delete_version

Tombstone one version of a key under this exact identity, or nothing when the row under it has moved on.

POST /v1/memory.delete_version · MCP loom_memory_delete_version · role writer · idempotent · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
memory_key str required The key to delete, under this exact identity: another agent's row of it is never read.
version int \| str required The version to delete, as memory.get answered it.
Output field Type Meaning
tombstoned bool True when the row still had version and a tombstone was written over it.
version int Nanosecond version of the tombstone row; 0 when nothing was deleted.
found bool False when no live row of this key exists under this identity.
reason str "version_changed" when the live row no longer carries version, or moved on while the delete ran (its tombstone then stays in the key's history, under the newer row).
dependents_tombstoned int Consolidated summaries built from this row (tag src:<key>): retired when none of their sources survive, else downgraded to conflicted.
dependents_downgraded int Summaries built from this row that keep a live source: set to conflicted.

memory.demonstrate

Test-time learning from memory: the nearest memories tagged as examples (default tag example, label carried as label:<value>), a similarity-weighted vote over their labels, the predicted label…

POST /v1/memory.demonstrate · MCP loom_memory_demonstrate · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str '*' The writing or reading agent; '-' or '*' means any agent where the op allows it.
query str required The input to classify; the example memories nearest to it are retrieved.
k int 8 At most this many labelled examples feed the vote and the block, 1-50.
example_tag str 'example' Only memories carrying this tag are examples.
label_prefix str 'label:' The label rides as a second tag: "
namespace_scope Literal['this', 'all_mine'] 'this' this searches namespace; all_mine also the caller's own namespaces (up to the fan-out cap), but labels are read from namespace only.
min_margin float 0.0 Below this normalised top-vote margin the prediction is reported but abstain is set, so a caller can fall back to its own reasoning.
max_block_chars int 6000 Character budget of few_shot_block; the nearest example is always included.
Output field Type Meaning
query str The query, echoed.
demonstrations list[DemonstrationRow] The labelled examples used, nearest first, at most k.
votes dict[str, float] label -> normalised similarity-weighted vote (sums to 1 when any)
predicted_label str The label with the largest vote; "" when no labelled example was found.
margin float The top vote minus the runner-up (votes sum to 1); 1.0 when only one label voted.
abstain bool True when no labelled example was found or margin is below min_margin; the prediction is still returned.
abstain_reason str no_examples, no_labelled_examples or low_margin; "" when not abstaining.
few_shot_block str The examples as Input: / Label: pairs for a prompt, nearest first, within max_block_chars.
examined int How many nearest memories were examined and how many carried a label.
labelled int How many of them carried a label (the demonstrations used, at most k).

memory.demote

Revert a shared memory back to private.

POST /v1/memory.demote · MCP loom_memory_demote · role admin · idempotent · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
memory_key str required The shared memory to tombstone.
rationale str '' Why it was demoted; stored on the tombstone row (empty keeps the promotion's rationale).
Output field Type Meaning
demoted bool False when the key has no live shared row; a demote outside the window still succeeds.
was_reversible bool whether within the reversal window
tombstoned_at datetime \| None When the shared row and its embedding were tombstoned; None when nothing was demoted.

memory.digests

List the session digests of a namespace.

POST /v1/memory.digests · MCP loom_memory_digests · role viewer · read

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '*' One session's digests; "*" or "-" = every session.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
limit int 200 At most this many digests, oldest session first; truncated says more exist.
Output field Type Meaning
rows list[_DigestRow] The live (not tombstoned) session digests, ordered by creation time then session id.
truncated bool True when more digests matched than limit returned.

memory.evidence

List the source turns a memory was extracted from, with the supporting span and whether each link was cited by the extractor, matched lexically, or fell back to the whole window.

POST /v1/memory.evidence · MCP loom_memory_evidence · role viewer · read

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
memory_key str required The memory whose evidence to list.
limit int 50 At most this many links (1–200), oldest first; truncated says more exist.
Output field Type Meaning
found bool False when no live row has the key under the caller's identity (or a wildcard lookup).
memory_key str The requested key, echoed; empty when not found.
memory_id str The resolved memory row's id.
value str The memory's value as text, clipped to 2000 characters.
items list[MemoryEvidenceItem] One item per live evidence link on this page, ordered by observed_at.
truncated bool True when the memory has more live links than limit.
cited int Counts by scope over ALL links (not just the returned page).
lexical int Links scoped lexical, over all links.
window int Links scoped window, over all links.
derived int Links scoped derived (consolidation lineage), over all links.

memory.export_evidence

The raw chunks and session digests behind a namespace's memories, as portable rows (paged).

POST /v1/memory.export_evidence · MCP loom_memory_export_evidence · role viewer · read

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
kind str 'chunks' edges pages the namespace's live operator_edges.
session_id str '' optional: one session only; "" or "-" = every session
limit int 500 Rows per page (1–2000); has_more says whether another page follows.
cursor str '' opaque keyset cursor from a previous page
include_high bool False Also export the chunks and digests behind a 'high' row or whose text holds a high shape (a credential, card number, SSN or stated PIN), and the chunks stored without a vector (one may hold part of a key).
Output field Type Meaning
chunks list[EvidenceChunk] kind: chunks: live chunks ordered by session then chunk index, each with its links.
digests list[EvidenceDigest] kind: digests: the latest live digest per session, ordered by session id.
edges list[EvidenceEdge] kind: edges: live typed links, ordered by (from_key, to_key, relation).
has_more bool True when more rows exist past this page.
next_cursor str Pass as cursor for the next page; empty on the last page and always for digests.
held_high int Chunks or digests of this page left out as 'high' (see include_high).

memory.exposure_summary

Per-(actor, agent) distinct memory keys returned by reads of a namespace over a window — how many distinct records each token has extracted — computed for every token, from…

POST /v1/memory.exposure_summary · MCP loom_memory_exposure_summary · role admin · idempotent · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
since_hours int 24 Look-back window in hours, 1 to 8760 (one year).
limit int 50 At most this many (actor, agent) rows, most distinct keys first; capped at 1000.
Output field Type Meaning
namespace_rows int Live (untombstoned) memories in the namespace: the denominator of fraction.
rows list[_ExposureSummaryRow] One row per (actor, agent) that read the namespace in the window, most keys first.

memory.extraction_retry_mine

Replay your failed deferred memory.set_from_messages batches once more, by queue_id.

POST /v1/memory.extraction_retry_mine · MCP loom_memory_extraction_retry_mine · role writer · idempotent · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
queue_ids list[str] required The ids deferred memory.set_from_messages calls returned.
Output field Type Meaning
requeued list[str] The asked batches accepted for one more attempt, in the order asked.
not_failed list[str] The asked batches you queued that had not failed (pending, running, held or done): nothing to replay.
model_calls_off list[str] The asked failed batches left failed because the org's model calls are switched off: a replay could only wait.
expired list[str] The asked failed batches queued 29 or more days ago: the queue drops them before its 30-day TTL, so a replay could not run.
unknown list[str] The asked ids no batch of yours answers to, as memory.extraction_status_mine's.

memory.extraction_status

State of deferred set_from_messages batches.

POST /v1/memory.extraction_status · MCP loom_memory_extraction_status · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str '*' The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
queue_id str '' One batch, by the id a deferred memory.set_from_messages returned; '' = every batch in scope.
status Literal['', 'pending', 'running', 'held', 'done', 'failed'] '' Only batches in this state, '' = any (held: its model call held for its org or its enqueuer's identity, no attempt spent).
limit int 50 At most this many batches returned.
Output field Type Meaning
batches list[_ExtractionQueueRow] The queued batches in scope: state, attempts, sizes and last error.
pending int Not yet extracted (pending + running + held) in the requested scope.
failed int Of the returned batches, how many ended failed.

memory.extraction_status_mine

Your deferred memory.set_from_messages batches, by the queue_id each call returned.

POST /v1/memory.extraction_status_mine · MCP loom_memory_extraction_status_mine · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
queue_ids list[str] required The ids deferred memory.set_from_messages calls returned.
Output field Type Meaning
batches list[_ExtractionQueueRow] The asked batches you queued, newest first: state, attempts, sizes and last error.
unknown list[str] The asked ids no batch of yours answers to, in the order asked: queued by another principal, in another namespace or outside the token's read scope, past the queue's 30-day retention, or never queued.
pending int Of the returned batches, how many are not extracted yet (pending, running, held).
failed int Of the returned batches, how many ended failed.

memory.for_file

What Loom knows about ONE file, shaped for the moment before an edit: memories whose files[] facet names the path (repo-relative or bare filename both match).

POST /v1/memory.for_file · MCP loom_memory_for_file · role viewer · read

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
path str required Repo-relative path ("src/Storages/MergeTreeData.cpp") or bare filename.
tags list[str] (computed) Optional tag filter (any-of), e.g. ["project:loom"] under the plugin's tag-scoped project mode.
top_k int 8 At most this many rows, across every namespace read.
token_budget int 300 Cap on the rendered block's rows other than decisions and constraints, which print whole outside it; the hook injects rendered verbatim.
include_conflicted bool True Conflicted rows are still live facts a reader should see; duplicates, superseded and rejected rows are not.
extra_namespaces list[str] (computed) The repository's primary code namespace (its team file rules and design decisions), read beside namespace.
Output field Type Meaning
path str The path matched, normalised to repo-relative form.
total int Number of hits returned (after the top_k cut).
hits list[_MemoryForFileHit] Matching rows: constraint-tagged first, then pinned, then by confidence, then newest; a row the conflict check linked to an older one of its namespace is placed above it (two typed decisions keep their order).
rendered str Prompt-ready block: decisions and constraints whole, the other rows cut at token_budget.
tokens_estimate int Size of rendered at 4 chars per token; 0 when it is empty.
truncated bool True when token_budget or the decision ceiling left rows out of rendered; hits still lists them.

memory.get

WHAT: Fetch one memory by exact memory_key.

POST /v1/memory.get · MCP loom_memory_get · role viewer · read

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
memory_key str required The key to read: tried under this identity, then the same user's row under any agent.
if_version int \| None None Version precondition: the version the caller already holds.
include_recall_breakdown bool False Opt-in: the per-agent read breakdown and served_count / last_served_at; off by default to keep get cheap.
as_of datetime \| None None As-of replay filter: restrict to rows with coalesce(valid_at, created_at) <= as_of.
fields list[str] (computed) A whitelist of output field names (value, tags, status, memory_type, recall_count, created_at, …).
follow bool False Serve the live canonical a status='duplicate' row points to (its duplicate_of, same org and namespace, up to three hops) instead of the exact row; the response then carries resolved_from = the key asked for and canonical_key = the…
Output field Type Meaning
found bool False when no live, unexpired row of this user holds the key.
unchanged bool True when if_version matched the live row: version below, no body.
memory_id str Stable row id, surfaced so a key-recalled row can record an outcome without a second lookup.
value dict[str, Any] \| list[Any] \| str \| int \| float \| bool \| None The stored value as written (text, object, list or scalar).
memory_type str \| None The row's memory type; None when not found.
confidence float \| None The writer's confidence in the fact, 0..1 (a demote expiry lowers it).
version int \| None Nanosecond version of the live row; send it back as if_version to poll.
version_text str \| None The same version as a decimal string, exact for a JavaScript caller (the number is above 2^53, which JSON.parse rounds): send this one to memory.delete_version.
created_at datetime \| None When the key was first written; kept across later versions.
updated_at datetime \| None Last write to this key; distinct from created_at, which is preserved across updates.
expires_at datetime \| None When the row expires (memory.get hides it after that, with as_of only a high one; memory.list serves it until the expiry sweep, and memory.bulk_get does too, except a high one); None = no expiry.
on_expire str Post-action on expiry ('' = tombstone) and its reason.
expire_reason str Why the expiry was set, as the writer gave it; '' when none.
valid_at datetime \| None Real-world time the fact became true (distinct from created_at = when recorded).
valid_to datetime \| None Closing bound of the fact's stated validity ("until 2024"); NULL = open interval, the common case.
temporal_anchor datetime \| None Date the fact is ABOUT (distinct from valid_at) and how precisely the source pinned it (0..1 per the extraction rubric; 0 = unscored).
temporal_confidence float How precisely the source pinned temporal_anchor, 0..1; 0 = unscored.
pinned bool True when the row was saved with memory.pin: a write that does not pin never replaces it (409 PINNED_CONFLICT).
actor str Who wrote the live version (human:<subject> / process:<subject>) and how the content entered memory (asserted | extracted | observed | derived | imported | external).
source_class str How the content entered memory: asserted | extracted | observed | derived | imported | external; '' when not found.
source_trace str The writer's provenance pointer, e.g. "conversation:"; '' when none.
source_resource_id str The resource the fact was lifted from (memory.provenance cites it); '' when none.
agent str Writer agent of the resolved row.
user_id str The resolved row's user and session: a write back to this row (restore, verify) names them, since a key repeats across users and sessions.
session_id str The session or conversation the row belongs to; '-' for none.
status str \| None Lifecycle state from loom.memory.status.
code list[CodeLink] The code this memory is linked to: each link's path, symbol, state (fresh, moved, changed, unresolved or missing) and the revise policy's suggestion.
superseded_by str For a status='superseded' row, the memory_id of the row that replaced it ('' otherwise) — the bundle turns it into a supersedes edge on the newer row.
duplicate_of str The row this one duplicates (status='duplicate'), and the verdict the row shows on read, derived from status (active → clear, conflicted → conflict, duplicate, superseded).
conflict_verdict str clear | conflict | duplicate | superseded, as described above.
canonical_key str \| None For a status='duplicate' row, the memory_key of the canonical that answers for it ("" when that row is no longer active — the orphan case memory.resolve_orphan_duplicates repairs).
resolved_from str \| None On an answer given by another row, the memory_key the caller asked for: a follow: true read that served a duplicate's canonical, or a memory.bulk_get chunk id answered by its parent fact.
tags list[str] Surfaced so an in-place tag edit can preserve existing tags/entities rather than clobbering them.
entities list[str] Entity names stored on the row (lowercased), internal sentinels left out.
content_sha256 str SHA-256 of the live value (canonical JSON); pass back as if_match_sha256 for a no-clobber update against the value THIS read saw.
char_limit int \| None Server-enforced char cap on value (NULL = no cap).
recall_count int Recall credits: reads by a credited reader (memory.search with auto_touch, from a client that posts outcomes), folded from loom.memory_usage_* into the ranking signal.
last_recalled_at datetime \| None When a credited read last returned the row; None = no credit yet.
served_count int \| None Agent reads that recalled the key, credited or not, never below recall_count; only with include_recall_breakdown=True, else None.
last_served_at datetime \| None When an agent read last returned the key; None = none recorded, or not asked for.
verified_by str Attestation (memory.verify).
verified_at datetime \| None When the attestation in verified_by was made; None = never attested, or revoked.
verification str human | process: attested and unchanged since; stale: the content changed after the attestation; none.
lock_mode str Lock in force (memory.lock). '' = unlocked or lapsed.
lock_reason str Why the lock was set, as the locker gave it; '' when unlocked.
lock_until datetime \| None When the lock lapses; None = open-ended, or no lock.
locked_by str Who set the lock (human:<subject> / process:<subject> under auth); '' when unlocked.
recall_by_agent list[_RecallByAgent] Agent reads of the key per named agent (last 30 days, credited or not); populated only when include_recall_breakdown=True.
sensitivity str '' | low | medium | high.

memory.get_by_id

Fetch a memory by its UUID (not memory_key).

POST /v1/memory.get_by_id · MCP loom_memory_get_by_id · role viewer · read

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
memory_id str required UUID returned by memory.set
as_of datetime \| None None As-of replay filter — see MemoryGetInput.as_of.
omit_high_value bool False True answers a 'high' row without its value or hash, and audits no sensitive read.
Output field Type Meaning
found bool False when no live row carries this id in the org (with as_of, one that was also valid then), the caller cannot read its namespace, or it can read more than one holding it (a restored namespace keeps the ids; use memory.get by key).
memory_id str The id asked for; '' when not found.
namespace str The namespace the op reads or writes.
agent str The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str The end user the row belongs to; '-' for none.
session_id str The session or conversation the row belongs to; '-' for none.
memory_key str The row's key; '' when not found.
memory_type str \| None The row's memory type; None when not found.
value dict[str, Any] \| list[Any] \| str \| int \| float \| bool \| None The stored value as written (text, object, list or scalar).
confidence float \| None The writer's confidence in the fact, 0..1.
version int \| None Nanosecond version of the live row.
created_at datetime \| None When the key was first written; kept across later versions.
expires_at datetime \| None When the row expires; None = no expiry.
valid_at datetime \| None Real-world time the fact became true; None when never supplied.
valid_to datetime \| None End of the fact's stated validity; None = open interval.
pinned bool True when the row was saved with memory.pin.
content_sha256 str SHA-256 of the live value (canonical JSON); pass back as if_match_sha256 on the next memory.update_by_id for a no-clobber update.
verified_by str Who attested the row (human:<subject> / process:<subject>); '' = none.
verified_at datetime \| None When that attestation was made; None = none.
verification str human | process: attested and unchanged since; stale: the content changed after the attestation; none.
sensitivity str '' | low | medium | high.
actor str Who wrote the live version and how it entered memory.
source_class str How the content entered memory: asserted | extracted | observed | derived | imported | external; '' when not found.
lock_mode str Lock in force and expiry post-action.
lock_reason str Why the lock was set; '' when unlocked or lapsed.
lock_until datetime \| None When the lock lapses; None = open-ended, or no lock.
locked_by str Who set the lock (human:<subject> / process:<subject> under auth); '' when unlocked.
on_expire str What the expiry sweep does when expires_at passes: tombstone | demote | anonymize; '' = tombstone.
expire_reason str Why the expiry was set, as the writer gave it; '' when none.

memory.health

Knowledge-base health for (org, namespace): how much of what is stored is ever recalled, what is silent, what earns its keep. agent + user_id are sentinels — the report aggregates across them.

POST /v1/memory.health · MCP loom_memory_health · role viewer · read

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str '-' The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
silent_after_days int 30 A live row with no recall credit and no agent read in this many days is silent; silent_older also needs the row to be older than this.
top_k int 10 How many entries top_recalled lists.
Output field Type Meaning
live int Active, untombstoned rows in the namespace, across every agent and user.
recalled_ever int Live rows with their own recall credit, or whose key an agent read recalled in the last silent_after_days days.
used_ever int Live rows with at least one reported use.
harmed_ever int live rows with at least one harm event
silent int live, no recall credit, and no agent read in the last silent_after_days
silent_older int silent AND older than silent_after_days
silent_share float silent / live, 0..1; 0 when the namespace holds no live row.
silent_after_days int Echo of the request's silent_after_days.
by_type list[MemoryHealthTypeRow] Per memory type: live rows and how many are recalled (as recalled_ever counts them), largest first.
top_recalled list[MemoryHealthTopRow] The live rows with the most recall credits (then use count), at most top_k.
staleness MemoryHealthStaleness Age buckets, validity-end, expiry and pin counts, and days since reinforced.
footprint MemoryHealthFootprint Summed value length of the live rows and its chars/4 token estimate.

memory.history

Enumerate the version history of one memory_key.

POST /v1/memory.history · MCP loom_memory_history · role viewer · read

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
memory_key str required The key whose versions are listed.
limit int 50 At most this many versions, newest first.
Output field Type Meaning
rows list[_MemoryHistoryRow] Every recorded version of the key, newest first, tombstoned and redacted ones included; empty when the key was never written.

memory.import

Import items another memory system distilled, through every check a write makes.

POST /v1/memory.import · MCP loom_memory_import · role writer · idempotent · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
entries list[_MemoryImportEntry] required The items to import, 1 to 200: each one's text, and optionally its tags, the time the source first knew it, its source pointer, a category and a sensitivity tier.
dry_run bool False Make every check and answer each entry's outcome; no memory, link or audit row is written.
Output field Type Meaning
totals _MemoryImportTotals How many entries ended each way.
results list[_MemoryImportResult] One result per entry, in request order.
dry_run bool True for a dry run: each outcome is what the import would do; no memory, link or audit row was written.

memory.import_curated

Split a curated markdown doc into sections, write each via handle_memory_set, returning per-section verdicts.

POST /v1/memory.import_curated · MCP loom_memory_import_curated · role writer · write · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
content str required Raw markdown; caller fetches the doc (the server doesn't read the filesystem).
source_uri str '' Stored in source_trace for lineage.
heading_level int 2 Heading depth to split on; h2 is the typical AGENTS.md / CLAUDE.md layout.
extra_tags list[str] (computed) Applied to every imported memory, on top of the auto source-kind + heading tags.
Output field Type Meaning
rows list[MemoryImportCuratedRow] One entry per heading section: its key, verdict and bytes written.
written int Sections written as memories.
merged int Sections the write marked as duplicates of an existing memory.
rejected int Sections refused because their key holds a pinned memory.
skipped int Sections not written: an empty body or another write error.

memory.import_evidence

Write exported evidence chunks and digests into a namespace, re-embedded here and linked to their memory keys.

POST /v1/memory.import_evidence · MCP loom_memory_import_evidence · role writer · write · audited

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
chunks list[EvidenceChunk] (computed) Chunks to re-embed and insert (≤256); empty-content or session-less ones are skipped, one that reads as high is inserted without a vector, and a key cut between two calls' chunks is not found.
digests list[EvidenceDigest] (computed) Session digests to re-embed and insert (≤256); empty summaries are skipped, and one whose text reads as high is inserted without a vector.
source_trace str 'okf-import' Stamped into memory_chunks.source_url when the chunk carries none.
Output field Type Meaning
chunks_written int Rows inserted into loom.memory_chunks, those written without a vector included.
links_written int Chunk-to-memory-key provenance links inserted from the chunks' memory_keys.
digests_written int Rows inserted into loom.session_summaries.
embedding_model str The server embedder the imported rows were re-embedded with.
embedding_dim int Vector length of the written embeddings; 0 when nothing was embedded (every item read as high, or none was written).
edited list[str] Chunks whose body did not hash to the content_sha256 they arrived with.
skipped int Items not written: empty, unembeddable, or refused by a blacklist pattern (Refuse).
unchecked int Of the skipped, those the blacklist could not check: the request's values are too long for its regexes; sent in a smaller request they are checked, unless one is too long alone, which must be shortened.

memory.inspect

Inspect one user, agent or session of a namespace, read-only.

POST /v1/memory.inspect · MCP loom_memory_inspect · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
subject Literal['user', 'agent', 'session'] required Which identity column id names: "user" (user_id), "agent" or "session" (session_id).
id str required The user_id, agent name or session_id to inspect; the unset sentinel "-" and "*" name no one and are refused.
sample_limit int 10 Newest rows listed per view, 0 to 50 (default 10).
window_seconds int 604800 Ledger window for the activity counts, in seconds (default 7 days, 1 hour to 90 days).
Output field Type Meaning
org str The tenant (first element of the identity tuple).
namespace str The namespace the op reads or writes.
subject Literal['user', 'agent', 'session'] The identity column inspected, as the request named it.
id str The user_id, agent name or session_id inspected.
computed_at datetime When the report was read (UTC).
own InspectView Rows whose identity names the subject.
others InspectView Every other live row of the namespace: what the subject reads as a member but did not write or is not filed under.
activity InspectActivity The calls the ledger files under the subject in the window.
settings InspectSettings The guide, categories and extraction policy that create the subject's memories.
skipped list[str] Sections that could not be read, as "section: reason".

memory.keys

Every live row's identity and key in one read: a set an exporter can check its paged listing against, since a row that moves while pages are read can be skipped.

POST /v1/memory.keys · MCP loom_memory_keys · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
limit int 100000 The most keys to answer; truncated says when the namespace holds more.
Output field Type Meaning
keys list[MemoryKeyRef] Every live row's identity and key, read by one statement, so the set is the namespace at one moment (memory.list's pages are read one at a time).
truncated bool True when the namespace holds more than limit live rows.

memory.lineage

Assemble the provenance chain for a SHARED memory.

POST /v1/memory.lineage · MCP loom_memory_lineage · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
memory_key str required The shared memory to trace; a key with no live shared row is read from loom.memory tags.
scope Literal['shared'] 'shared' Only shared is accepted.
candidate_match_window_s int 300 Window (s) each side of promoted_at for the best-effort learnings_queue match.
Output field Type Meaning
found bool False when the key has no live row in loom.shared or loom.memory.
memory_key str The requested key, echoed.
promotion_event str Promotion-event metadata, lifted off the loom.shared row.
promoted_by str Who promoted it: auto for the auto-promote worker, else the approver; for a memory-only row, the row's user_id.
promoted_at datetime \| None When it was promoted; for a memory-only row, the row's created_at.
rationale str The promotion's rationale; for a memory-only import, Imported via <source kind>.
source_trace str Direct source pointers when the row came from a conversation/document/import resource rather than a shared-memory promotion.
source_resource_id str The source resource id stored on the memory row; empty for a shared-row walk.
mechanism Literal['auto', 'review', 'import', 'unknown'] auto = auto-promote worker; review = learnings.review; unknown = unmatched.
supporters list[_LineageSupporter] The identities the shared row was promoted from; empty for a memory-only row.
candidate _LineageCandidate \| None None for mechanism='auto' (bypasses the queue), GC'd queue rows, or a missed timing window.
audit_chain list[_LineageAuditEvent] Up to 100 audit rows carrying the promotion event as request id; empty without one.

Author an explicit edge between two memories (or two entities).

POST /v1/memory.link · MCP loom_memory_link · role writer · write · audited

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
from_key str required Typically memory_keys, but bare entity strings work too; graph_mode SQL treats both endpoints uniformly.
to_key str required The edge's object endpoint (a memory key or an entity string).
relation str '' Relation verb, canonicalized on write (memory/domain/edge_relations.py): supports, depends_on, caused, contradicts, refines, implements, validates, supersedes, blocks, fixes, derived_from, related_to.
label str '' Free-text label stored on the edge and returned by memory.provenance.
explanation str '' Why this edge exists, in one human-readable sentence, and the concrete observations that support it.
evidence list[str] (computed) Up to 10 supporting observations; each is trimmed to 500 chars, blanks dropped.
confidence float 1.0 Confidence that the RELATIONSHIP holds — not confidence in either memory.
run_id str '-' The run that asserted the edge (run.begin); joins the edge to its ledger.
weight float 1.0 edge strength; higher = stronger preference at graph-mode rank time
tombstone bool False rescind a prior edge (writes a tombstone row)
Output field Type Meaning
linked bool True when an edge was written; False when the call wrote a tombstone.
from_key str The written row, echoed so the caller can chain. from/to are the canonical direction (a reversed verb such as blocked_by is flipped).
to_key str The edge's object endpoint as stored, after any direction flip.
relation str canonical verb as stored
confidence float The relationship confidence as stored, echoed.
conflict_opened bool relation == contradicts and both endpoints are live memories: both rows are now status='conflicted' and appear in conflicts.list.
relation_defaulted bool The caller passed no relation; the row was stored as related_to.

memory.list

List memory keys under this identity, optionally filtered.

POST /v1/memory.list · MCP loom_memory_list · role viewer · read

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
prefix str \| None None Only keys that start with this string; None or '' = every key.
memory_type str \| None None Only rows of this memory type; None = every type.
source_trace str \| None None Exact-match on source_trace — e.g. "conversation:" to list the memories a given conversation produced.
limit int 100 At most this many entries; truncated says when more exist.
offset int 0 Page offset for paginated browsing (recency sort).
count_only bool False The two counts (total, today) and the namespace version, no rows — for a caller that shows a number (the Setup page's namespace total).
preview_chars int 2048 The preview length the caller shows.
sort_by Literal['recency', 'score'] 'recency' recency (newest first) or score (evidence_count + 0.5 × recall_count, highest first).
quotas dict[str, int] \| None None With sort_by: score, the most entries per memory type ({"principle": 5}); a type not named, or at 0, is uncapped.
include_tombstoned bool False Also return tombstoned rows (within the 14d grace window) for recovery views.
include_duplicates bool False status='duplicate' rows are skipped (their canonical is in the same scan) unless asked for.
include_superseded bool False status='superseded' rows (a later write replaced them) are skipped unless asked for; a served entry carries its status.
include_proposed bool False status='proposed' rows (facts on trial) are skipped unless asked for; memory.proposals lists them with their graded counts.
as_of datetime \| None None see MemoryGetInput.as_of
tags list[str] (computed) Only rows carrying any one of these tags, in any case; empty = no tag filter.
tags_every bool False Require every one of tags instead of any one; default off.
contains str \| None None Only rows whose value or key holds this text, any case (a high row by its key only) — the keyword filter of a browse page's search box, which must find every row that names a word (a ranked search returns its top k and misses the rest).
fields list[str] (computed) Whitelist of entry field names; every entry carries only those plus memory_key and sensitivity.
known_version int \| str \| None None The namespace_version a previous answer carried.
Output field Type Meaning
entries list[_MemoryListEntry] The matching rows in the requested order; empty on an unchanged poll.
truncated bool true if more entries exist beyond limit
total int Total rows matching this scope (ignores limit), so callers can show "N of TOTAL". 0 on an unchanged poll, which carries no counts.
today int Of total, how many were created since UTC midnight today — the true "today" count (not the truncated page count the day-group headers show).
namespace_version str The namespace's newest memory version (any agent, any status — every write moves it).
unchanged bool True when known_version matched: entries, total and today were not read and are their defaults.
hidden int When a prefix listing is empty, how many rows under the prefix it left out as duplicate/superseded, and the flag that serves them. 0 / "" otherwise.
hidden_hint str A sentence naming those hidden rows and the flag that lists them; '' otherwise.

memory.lock

Lock one row against a class of writes.

POST /v1/memory.lock · MCP loom_memory_lock · role writer · write · audited

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
memory_key str required The key to lock, under this exact identity (a miss answers locked: false).
mode Literal['read_only', 'no_delete'] 'read_only' read_only refuses a changed value, a changed expiry and deletion; no_delete lets the value change but refuses tombstone, redact, clear, consolidation and reject.
reason str required Why the row is locked (1..512 chars); a refused write's 423 quotes it.
until datetime \| None None When the lock lapses (must be in the future); None and no ttl_seconds = open-ended.
ttl_seconds int \| None None Lock for this many seconds from now; wins over until when both are sent.
locked_by str '' Locker recorded when the server runs without auth; under auth the token subject is recorded instead.
Output field Type Meaning
locked bool False when the key doesn't exist (or is tombstoned)
reason str Why locked is False — "not_found" (no live row at this key: never written, tombstoned, or merged into another key).
memory_id str Stable id of the locked row; '' when not found.
version int \| None Nanosecond version of the lock row written; None when not found.
lock_mode str The mode now in force (read_only / no_delete); '' when not found.
lock_until datetime \| None When the lock lapses; None = open-ended, or not found.
locked_by str Who holds the lock (human:<subject> / process:<subject> under auth); '' when not found.
previous_mode str '' when the row was unlocked before this call

memory.move

Rename from_key to to_key, leaving the old key answering.

POST /v1/memory.move · MCP loom_memory_move · role writer · write · audited

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
from_key str required The key to rename.
to_key str required The key the row moves to.
value Any None Rewrite the value on the destination (e.g. the claim-first text that replaces a JSON blob).
memory_type str \| None None Default: the source's.
tags list[str] \| None None Default: the source's.
entities list[str] \| None None Default: the source's.
valid_at datetime \| None None Default: the source's.
source_resource_id str '' Default: the source's.
keep_redirect bool True true: the old key stays readable (memory.get(from_key) returns the old row with canonical_key = to_key; follow: true serves the new row). false: the old key is tombstoned instead.
if_match_sha256 str '' Precondition on the SOURCE row's content hash (409 PRECONDITION_FAILED on mismatch), the memory.set semantics.
dry_run bool False Report what would happen — destination key, type, value preview, redirect or tombstone — without writing.
Output field Type Meaning
from_key str Echo of the source key.
to_key str Echo of the destination key.
redirected bool True when the source row now redirects to the destination; False when it was tombstoned (keep_redirect: false) or nothing was written (dry run).
memory_id str The destination row (empty / 0 on a dry run).
version int The destination write's version, as memory.set returned it.
content_sha256 str SHA-256 of the destination value, as memory.set returned it.
advisories list[str] memory.set's advisories on the destination write, passed through.
dry_run bool Echo of the request's dry_run.
memory_type str Dry-run preview of the destination: its type and the first 200 chars.
value_preview str Dry run only: the first 200 chars of the destination value; "" for a high source, whose value is read by key (memory.get).
sensitivity str Dry run only: the source's sensitivity tier ('' | low | medium | high).

memory.outcome

External outcome signal for a memory row.

POST /v1/memory.outcome · MCP loom_memory_outcome · role writer · write · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
memory_id str '' Address the row by stable id OR by memory_key (resolved server-side).
memory_key str '' The row's key, used when memory_id is empty; resolved under agent/user_id.
agent str '*' The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
outcome Literal['positive', 'negative', 'used'] required positive adds evidence, negative records harm (and counts toward the auto-quarantine threshold), used adds search-side reinforcement.
weight int 1 How many events this outcome counts as.
source str '' optional caller-supplied tag
Output field Type Meaning
recorded bool False when the row was not found or the usage write failed.
evidence_count_after int evidence_count after the bump (0 if unknown)
quarantined bool Set when a negative outcome crossed memory_negative_demote_threshold and the memory was auto-quarantined (status -> 'rejected'); a locked memory is not.

memory.outcome_run

Apply one outcome to many recalled memories in a single call — the bulk form of memory.outcome, so callers skip a per-key loop.

POST /v1/memory.outcome_run · MCP loom_memory_outcome_run · role writer · write · audited

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
outcome Literal['positive', 'negative', 'used'] required positive records evidence, negative harm, used reinforcement on each key.
memory_keys list[str] (computed) The recalled keys to apply the outcome to; empty derives them from run_id.
run_id str '' The run whose recalls supply the keys when memory_keys is empty; "" or "-" names no run.
weight int 1 How many events the outcome counts as on each key.
source str '' Optional caller-supplied tag; not stored on the usage events.
max_keys int 1000 At most this many keys are applied; the rest are dropped.
Output field Type Meaning
applied int rows an event was recorded for (evidence / used / harm)
keys_matched int distinct keys recalled under the run
run_id str The agent run (task scope) the call belongs to; '-' for none.

memory.outline

A namespace's table of contents, read from structure alone: row counts by type, the dated sessions with their digest's first line, the hub entities by degree, the communities when a pass has computed…

POST /v1/memory.outline · MCP loom_memory_outline · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
sessions_limit int 50 At most this many sessions, newest first; 0 skips the session read.
hubs_limit int 12 At most this many hub entities, highest degree first; 0 skips the read.
communities_limit int 8 At most this many communities from the latest pass, largest first; 0 skips it.
Output field Type Meaning
rows_by_type dict[str, int] Count of live, active rows per memory_type; document chunks are not counted.
sessions list[_OutlineSession] Sessions with a digest, newest first: date, the digest's first sentence, its key-fact count and how many memory keys it compressed.
sessions_total int Number of live session digests in the namespace; 0 when sessions_limit is 0.
hubs list[_OutlineHub] The entities with the most graph edges, with their degree.
communities list[_OutlineCommunity] Top-level entity communities from the most recent community pass; empty when no pass has run.
profile_chars int Length of user_id's newest profile row; 0 when there is none or user_id is -.

memory.owner_report

The owner's view of their memory: what they own, who read it, how it was used.

POST /v1/memory.owner_report · MCP loom_memory_owner_report · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
user_id str \| None None The end user the row belongs to; '-' for none.
namespace str \| None None The namespace the op reads or writes.
since_hours int 720 Look-back window in hours for reads, 1 to 8760; rows and usage are all-time.
limit int 50 At most this many reads rows, most calls first; capped at 1000.
Output field Type Meaning
user_id str The end user the row belongs to; '-' for none.
since_hours int The reads window the report used, echoed from the request.
live_rows int Active, untombstoned memories the owner has across the readable namespaces.
rows list[_OwnerRowsRow] The owner's active memories counted per (namespace, memory_type).
reads list[_OwnerReadsRow] Who read the owner's memories in the window: per (namespace, agent, actor), calls, distinct keys and last read, most calls first.
usage list[_OwnerUsageRow] Per namespace, the recall / used / harm totals of the owner's memories and the last recall time.

memory.pin

WHAT: Save a standing fact and protect it.

POST /v1/memory.pin · MCP loom_memory_pin · role writer · idempotent

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
memory_key str required The key to write; a write to a key that already holds a row adds a new version.
memory_type str 'semantic' Coarse classification axis (drives procedural memory + tag_registry policy lookups).
value dict[str, Any] \| list[Any] \| str \| int \| float \| bool required The fact itself: plain text, or a JSON object, list or scalar.
tags list[str] (computed) Flexible classification, filterable at recall via MemorySearchInput.tags; per-tag retention/dedup/conflict policy via NamespaceRegisterInput.tag_registry.
entities list[str] (computed) Explicit entities for a point write.
relations list[tuple[str, str, str]] (computed) The (subject, relation, object) facts the value states, written to the fact graph and linked to the row as a memory.bulk_set entry's are: a single-valued fact closes the older one it replaces and, under newest_wins, retires the rows…
files list[str] \| None None The repo files this memory is about (repo-relative path or bare filename); memory.for_file serves it before an edit of them.
confidence float 1.0 The writer's own confidence in the fact, 0..1 (1.0 = certain).
source_trace str '' Provenance tag of the write (conversation:<id>, curated_doc://…); a known prefix decides source_class (conversation: → extracted), and one starting with memory.delete_version: is refused.
source_class str \| None None Declare that this content came from outside (a tool result, a page, a document): external, or imported.
source_resource_id str '' Provenance pointer to the primary source this fact was cited from — a file path, document id, or URL.
ttl_seconds int \| None None Expire the row this many seconds from now.
clear_expiry bool False Force expires_at=NULL, ignoring per-type/namespace retention defaults.
on_expire Literal['', 'tombstone', 'demote', 'anonymize'] '' What happens when expires_at passes: '' keeps the row's current action (tombstone for a new row); 'demote' clears the expiry and scales confidence down; 'anonymize' replaces the value with a marker and drops the embeddings, keeping the…
expire_reason str '' Why the expiry is set (at most 512 chars, not starting with blacklist.sweep:); '' keeps the row's current reason.
valid_at datetime \| None None Real-world time the fact became true (distinct from created_at and from temporal_anchor).
valid_to datetime \| None None Closing bound of the fact's stated validity ("until 2024"); NULL = open interval.
pinned bool False True when written via memory.pin (explicit-save)
classify bool False Call the namespace's classify_endpoint to derive memory_type from value.
if_match_sha256 str '' Optimistic-concurrency precondition: 409 PRECONDITION_FAILED if the live row's SHA-256 doesn't match.
char_limit int \| None None Server-enforced char cap on the stringified value (422 on exceed).
sensitivity Literal['', 'low', 'medium', 'high'] '' Sensitivity tier. 'high' = never embedded, searched or in a recency pack; read by key only, audited. '' = detect from the value: credentials, card numbers, SSNs, PINs high; email, phone, IBAN medium.
dry_run bool False Run the conflict check and return its verdict, peers and next WITHOUT writing — no loom.memory row, no embedding row, no audit hash, no usage event, no supersession.
proposed bool False Write the fact as a PROPOSAL (status='proposed'): not served by default, served to a read that passes include_proposed, and accepted or rejected on the graded outcomes of the runs that used it (run.grade, memory.proposals…
Output field Type Meaning
version int nanosecond version assigned by the server
pinned bool True when the stored row is pinned: a memory.pin, or a restatement of a pinned row.
memory_id str UUID assigned to this insert; usable with memory.get_by_id
content_sha256 str SHA-256 of the just-written value; usable as if_match_sha256 on next write.
status str The written row's lifecycle status (active, duplicate, proposed); "" on a write that stored no row (merge, dry run).
duplicate_of str Set when this write was tagged status='duplicate': memory_id of the canonical semantically-identical row.
namespace str Echoed so a tool-call response carries citable factual data.
memory_key str The key the request named; on a merge see merged_into_key.
merged_into_key str On a merge the store already held this content under ANOTHER key; nothing was written at memory_key, so a get/lock/update of it finds no row.
degraded list[str] Providers that fell back on this write and what the response did instead (classify: caller_type_kept (ReadTimeout)).
retyped_from str Set when the declared memory_type did not fit the value and the row was stored under another type (currently: 'procedural' on a value that is not a procedure body → 'semantic', tagged retyped_from:procedural).
conflict_verdict str What the write-time conflict check decided about THIS write — clear | merge | duplicate | conflict | refines | rejected (the last only on a dry run; a real write answers 409).
peers list[MemorySetPeer] The rows the verdict was reached against (closest first, at most five): the key is the handle for memory.update, the excerpt what the store holds.
next str A short machine hint naming the op to reach for: memory.update(key) on a conflict, memory.pin on a merge the store has seen before, "" otherwise.
noop bool True when this call wrote nothing: a merge (the store already held the fact; its row was reinforced instead) or a dry run.
dry_run bool Echo of the request's dry_run so a tool-call transcript is unambiguous.
advisories list[str] One line per thing the server noticed about the VALUE that the agent should fix on its side — currently only "base64-encoded".
chunks_written int Verbatim chunk rows this write added to the chunk plane for a long searchable value (0 for a short value, a non-searchable type, a merge or a dry run).

memory.promote

Promote a single source memory to the namespace's shared layer.

POST /v1/memory.promote · MCP loom_memory_promote · role admin · write · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
memory_key str required source key in loom.memory
target_key str \| None None default = memory_key
rationale str '' Why it was promoted; stored on the shared row and returned by memory.lineage.
approver str \| None None Stored as the shared row's promoted_by; None records explicit.
reversible_for_days int 7 Days (0–90) the promotion counts as reversible; memory.demote reports it.
promoted_from_candidate_id str \| None None On learnings.review-driven promotions, written to loom.shared.promoted_from_candidate_id so memory.lineage resolves via an FK-style lookup.
Output field Type Meaning
shared_memory_key str The key the shared row was written under (target_key, else the source key).
promotion_event str UUID, also the audit_log request_id
reversible_until datetime End of the reversal window: now plus reversible_for_days.

memory.proposal_decide

Accept (→ active, served by default) or reject a list of proposals.

POST /v1/memory.proposal_decide · MCP loom_memory_proposal_decide · role writer · idempotent · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
memory_keys list[str] required Keys of the proposals to decide; a key that is not a proposal is left as is and reported.
decision Literal['accept', 'reject'] required accept makes each proposal live (active, or conflicted when contradicted on trial); reject sets it rejected.
reason str '' Why; stored as the rejection reason on reject ("proposal rejected" when empty).
Output field Type Meaning
decided int How many keys were decided.
results list[MemoryProposalDecision] One entry per key: decided or not, its status after, and why not.

memory.proposals

List facts on trial (written with memory.set proposed=true) with the graded runs that relied on each — passes, fails — and what the namespace's proposal policy says (ready: accept / reject).

POST /v1/memory.proposals · MCP loom_memory_proposals · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
prefix str '' Narrow the listing: a key prefix (lesson_), any-match tags.
tags list[str] (computed) Only proposals carrying any of these tags; empty = no tag filter.
only Literal['', 'ready'] '' ready keeps only proposals the policy thresholds would decide now (accept or reject); "" lists every proposal.
limit int 50 At most this many proposals returned, newest first.
Output field Type Meaning
proposals list[MemoryProposal] The proposals in scope, newest first, with graded counts and ready.
total int Proposals in scope before only and limit.
policy ProposalPolicy The namespace's proposal policy: mode (manual / auto) and thresholds.

memory.provenance

Resolve a memory's origin for user-facing provenance.

POST /v1/memory.provenance · MCP loom_memory_provenance · role viewer · read

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
memory_key str required The memory to trace.
Output field Type Meaning
found bool False when no live row has the key under the caller's identity; the rest is then empty.
source_trace str The row's raw source_trace as written; empty for a memory added manually.
source_kind str conversation | extracted | observed | consolidated | app | manual | other (derived at read time from the source_trace convention)
actor str Stored at write time: who wrote the live version and asserted | extracted | observed | derived | imported | external.
source_class str The row's stamped provenance class (asserted, extracted, …); see actor above.
source_label str human-readable origin
thread_id str The conversation thread id, set only when source_trace is conversation:<id>.
thread_exists bool Whether the source conversation transcript is still retained (recording may be off per-namespace, or the 180-day conversation TTL may have lapsed).
thread_title str The retained thread's title; empty when the thread is gone or untitled.
agent str The writing or reading agent; '-' or '*' means any agent where the op allows it.
session_id str The session or conversation the row belongs to; '-' for none.
run_id str The agent run (task scope) the call belongs to; '-' for none.
written_at str The live version's created_at, as a string.
source_resource_id str Caller-supplied pointer to the primary cited source (file/doc/URL).
links list[MemoryProvenanceLink] Authored "derived_from"/etc. edges (memory.link) touching this memory.

memory.recall

Search the raw conversation turns behind this namespace's memories — the messages memory.set_from_messages extracted from — by text and by time.

POST /v1/memory.recall · MCP loom_memory_recall · role viewer · read

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
query str '' Text to match in turns: lowercased tokens of 2+ characters, stop words dropped, at most 32 kept; empty reads by time window or thread alone.
since datetime \| None None Inclusive lower bound on the turn's observed time; None = unbounded.
until datetime \| None None Inclusive upper bound on the turn's observed time; None = unbounded.
thread str '' Restrict to one conversation (the session_id the turns were ingested under).
role Literal['', 'user', 'assistant'] '' Only turns of this speaker role; empty returns turns of every role.
match Literal['all', 'any'] 'all' all: a turn must carry every query token; any: at least one of them.
order Literal['auto', 'recent', 'relevance'] 'auto' recent = newest first, relevance = most matched tokens first, auto = relevance with a query, else recent.
top_k int 20 At most this many turns per page.
cursor str '' next_cursor of the previous page in newest-first order; empty starts at the top.
offset int 0 Turns to skip in relevance order; pass the previous page's next_offset.
max_chars int 600 Per-turn content cap; a long turn is cut and flagged truncated.
read_scope Literal['namespace', 'own_user', 'own_session'] 'namespace' Same semantics as memory.search.read_scope.
Output field Type Meaning
turns list[_RecallTurn] The matching turns of this page, each with content cut to max_chars.
total_matched int Turns matching the filter in the whole namespace scope, not just this page: "showing 20 of 124".
returned int Number of turns in this page.
has_more bool True when another page exists after this one.
next_cursor str Newest-first paging token; empty when exhausted or in relevance order.
next_offset int Relevance paging offset; -1 when exhausted or in newest-first order.
order Literal['recent', 'relevance'] The order actually used, after auto is resolved.
query_tokens list[str] The tokens the query was reduced to and matched on.
tokens_returned int Estimated prompt tokens of the returned content (chars / 4).

memory.record.conflicts

Field-level disagreements recorded when a typed record superseded the active record for its logical key: which field, both values, both record ids and both citing memories.

POST /v1/memory.record.conflicts · MCP loom_memory_record_conflicts · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
schema_name str '' Only this schema's conflicts; empty = every schema.
memory_key str '' Only this logical key's conflicts; empty = every key.
open_only bool True True hides conflicts with a resolved_at.
limit int 100 At most this many rows (1–1000), newest detection first.
Output field Type Meaning
rows list[MemoryRecordConflictRow] Field-level disagreements, newest detection first.
truncated bool True when more rows matched than limit.

memory.record.history

Every version of one typed record, newest first.

POST /v1/memory.record.history · MCP loom_memory_record_history · role viewer · read

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
record_id str required The record whose stored versions are listed.
limit int 50 Accepted and bounded (1–500), but the handler does not apply it.
Output field Type Meaning
rows list[MemoryRecordRow] One row per stored version of the record, newest first.

memory.record.query

Typed records of one schema by field filters and predicates, as of a time.

POST /v1/memory.record.query · MCP loom_memory_record_query · role viewer · read

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
schema_name str required The schema whose active records are read.
filters dict[str, str] (computed) Field name → exact string value (state value); all must match.
predicates list[RecordPredicate] (computed) Typed comparisons or state checks, AND-composed with filters.
as_of datetime \| None None Only records whose valid_at..valid_to interval covers this instant; None = no bound.
limit int 100 At most this many records (1–1000), most recently updated first.
Output field Type Meaning
rows list[MemoryRecordRow] Matching active records, most recently updated first.
truncated bool True when more records matched than limit.

memory.record.write

Write one typed record (schema name + fields, optional validity window); a newer record for the same key supersedes the old.

POST /v1/memory.record.write · MCP loom_memory_record_write · role writer · write · audited

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
schema_name str required An active schema of the namespace, else 404 SCHEMA_NOT_FOUND.
memory_key str '' The logical key; empty derives it from identity fields, or leaves the record anonymous.
memory_id str \| None None The memory row this record cites; stored on the record and on its conflict rows.
agent str '' The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
fields dict[str, RecordFieldInput] required Field name → value and state; names outside the schema or missing required ones are 400.
valid_at datetime \| None None When the fact became true in the world; closes the superseded record's valid_to.
valid_to datetime \| None None When the fact stops being true; None = open-ended.
Output field Type Meaning
record_id str The new record's id; every write mints a fresh one.
field_count int Typed field rows written, including fields kept from the superseded record.
memory_key str The logical key the record landed on: the caller's, or one derived from the schema's identity fields, or "" (anonymous — no supersession).
key_source Literal['caller', 'identity', 'anonymous'] Where memory_key came from: the caller, the identity fields, or none.
fold dict[str, Any] Supersession is a field-level fold, not a whole-record replace: {agreed, filled_from_prior, added, unioned, changed:[{field, prior, incoming}]} against the record this write superseded; empty when there was none.
superseded_record_id str The key's previously active record, now superseded; empty when there was none.
conflicts int Field disagreements with the superseded record written to the conflicts table.

memory.redact

Scrub the value of a historical memory row, preserving its audit entry.

POST /v1/memory.redact · MCP loom_memory_redact · role admin · idempotent · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
memory_id str required The id every version of the memory shares; version picks the historical row.
version int \| str required The target row's version, from memory.set or memory.history.
reason str '' short justification; the audit row keeps its SHA-256 and length
redacted_by str '' actor for the audit row (caller-supplied)
Output field Type Meaning
redacted bool False when memory_id or that version is not found, or the caller is not admin of its namespace.
refused_head bool True when the target was the live head
redacted_at datetime \| None UTC time of the scrub; None when nothing was redacted.
cache_rows_deleted int The cached answers (the model response cache and code.ask's) whose window or quoted memory keys held the redacted key or a profile key the redact reached, deleted with it: the stored text that quoted the value.
precedent_rows_deleted int Stored memory.ask precedents that quoted the redacted key or a profile key the redact reached, deleted with it, across the org for a profile key.

memory.reindex

Re-embed a namespace's memories whose vectors were made by another embedding model (or all of them with force).

POST /v1/memory.reindex · MCP loom_memory_reindex · role admin · idempotent · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
limit int 200 Max rows per call (bounded to avoid spiking the embedder / holding CH locks).
force bool False Also re-embed rows already matching the current model (force a clean rebuild).
Output field Type Meaning
processed int Rows re-embedded in this call; call again until it is below limit.
skipped_already_current int Always 0: rows already on the current model are not read unless force.
target_model str The embedding model the rows were re-embedded under (the current one).
target_dim int The vector dimension of that model.

memory.reject

Mark a memory as explicitly rejected (extractor / user review decided the candidate fact is wrong), so it isn't re-extracted.

POST /v1/memory.reject · MCP loom_memory_reject · role writer · idempotent · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
memory_key str required The memory to mark rejected, matched on org, namespace, agent, user and key.
rejection_reason str '' Stored as the row's rejection_reason; empty stores none.
Output field Type Meaning
rejected bool True on every 200 answer, also when no live row matched (nothing is written then); a locked row in any session refuses the whole reject with 423 LOCKED.
rejected_at datetime \| None The server time of the reject.

memory.related

Find memories sharing entities with the source memory.

POST /v1/memory.related · MCP loom_memory_related · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
memory_key str required The source memory, looked up under the caller's full identity tuple.
top_k int 10 At most this many related memories (1–100), most shared entities first.
min_overlap int 1 Min overlapping entities to qualify. 1 = any shared entity.
Output field Type Meaning
results list[_MemoryRelatedHit] Live memories sharing entities with the source (the source excluded), by overlap.
source_entities list[str] The entities on the source row; empty when it is missing or has none.
reason str Reason code when results=[] for a precondition (not an empty result set): "source_not_found" | "source_has_no_entities".

memory.resolve_orphan_duplicates

Scan (org, namespace) for orphaned duplicate rows and re-activate them.

POST /v1/memory.resolve_orphan_duplicates · MCP loom_memory_resolve_orphan_duplicates · role writer · idempotent · audited

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
limit int 500 Max orphan rows to re-activate per call.
dry_run bool False True lists the orphan candidates without re-activating them.
Output field Type Meaning
candidates int Orphan duplicates the scan matched.
reactivated int Rows re-emitted status='active' (0 under dry_run).
dry_run bool Echo of the request's dry_run.
memory_ids list[str] memory_ids of the affected (candidate) rows.

memory.restore

Bring back a deleted memory (within 14 days) or one Loom retired on its own.

POST /v1/memory.restore · MCP loom_memory_restore · role writer · idempotent · audited

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
memory_key str required The key to restore, under this exact identity (no namespace-wide fallback).
if_superseded_by str '' Precondition: restore only a row still retired behind this memory, as a memory.auto_supersede record names it; a row deleted, revived or retired behind another memory since is left as it is.
only_if_superseded bool False Precondition: restore only a row still retired (superseded), never undo a delete.
Output field Type Meaning
restored bool True when a live version was written back; False on a no-op, a miss or expired.
memory_id str \| None Empty / None when nothing was tombstoned or superseded for this key (no-op).
version int \| None Version of the restored row, or of the already-live row on a no-op.
expired bool Surfaces "you missed the window" cleanly rather than 404-ing.
redacted bool True when the deleted row's text was redacted: there is nothing to bring back.
from_status str What the row was restored FROM: "tombstoned" (a delete undone) or "superseded" (an automatic retirement reversed).

memory.search

Semantic search over the agent's own memory.

POST /v1/memory.search · MCP loom_memory_search · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str '*' Defaults to "*" = read ALL agents in the accessible namespaces: membership is the read boundary, agent is authorship metadata.
render Literal['', 'reader'] '' render="reader": also return reader_block + reader_instructions, the hits rendered and instructed the way memory.ask reads them.
query str required The natural-language question or keywords to search for; it is embedded for the semantic legs and tokenized for the lexical ones.
memory_type Literal['semantic', 'procedural', 'preference', 'reflection', 'decision'] \| None None None = every searchable type except the hidden-by-default ones (reflection). "reflection" = only process lessons (what worked / failed in past runs), which never surface in an unfiltered search.
tags list[str] (computed) OR-filter on tags (set semantics).
scope Literal['private', 'shared'] 'private' Which rows to read: "private" = per-agent rows (narrowed to agent when it names one), "shared" = rows promoted to the namespace's shared scope.
namespace_scope Literal['this', 'all_mine'] \| None None "this" reads input.namespace only; "all_mine" also the caller's own namespaces, see docs/concepts/identities-and-namespaces.md#reading-several-namespaces.
search_mode Literal['semantic', 'lexical', 'hybrid', 'bm25_hybrid', 'related', 'graph', 'rrf', 'temporal'] 'rrf' Recall fusion mode.
recipe Literal['', 'fast', 'balanced', 'quality', 'verbatim', 'code', 'temporal', 'graph'] '' Named presets over the lower-level knobs.
alpha float 0.5 Lexical weight for search_mode "hybrid" / "bm25_hybrid": the score is alpha * lexical + (1 - alpha) * cosine.
include_top_n_unmatched int 0 When > 0 and a reranker is configured, fetch this many extra candidates by recency to widen the reranker's pool.
include_tags bool False Carry each hit's tags on the response.
include_usage bool False Carry each hit's reliance counters on the response — reinforcement_count (the folded rank signal), used_count (an agent's label: memory.outcome used / run.complete memories_used), used_cited_count (memory.ask's own credit for citing…
include_duplicates bool False A status='duplicate' row (set-time dedup at 0.97 cosine named its canonical in duplicate_of) is not served; its canonical takes the slot, disclosed on the hit as resolved_from.
include_superseded bool False A status='superseded' row (a later write replaced it) is not served unless asked.
include_proposed bool False status='proposed' rows (facts written for trial, accepted or rejected on graded run outcomes) are served only here, when asked.
explain bool \| None None Carry per-hit explanation and score_components.
legs list[Literal['memory', 'sessions']] \| None None The legs the caller can show.
auto_touch bool True Record append-only recall usage for every returned row (powers recall_count / last_recalled_at via loom.memory_usage_*, no read-path mutation of loom.memory).
user_id str \| None None filter by user_id (for preference recall)
run_id str \| None None only rows written under this run; unset or "-" reads every run
session_id str \| None None Filter by session_id.
session_ids list[str] (computed) Restrict to a set of sessions: a caller may pin its own.
exclude_keys list[str] (computed) Keys the caller already holds and does not want served again: dropped from the ranked pool BEFORE the window is assembled, so the window fills from the next-best candidates instead of returning a shorter list.
quality_filter bool False Drop stored rows that are not facts (trajectory steps, session narration, tool tips, code-location trivia, probes, one-moment status — server/memory/domain/fact_quality.py) and restatements of a higher-ranked fact from the pool before the…
read_scope Literal['namespace', 'own_user', 'own_session'] 'namespace' How far this read may reach inside each namespace: "namespace" — every row the token can access (membership is the read boundary; the default). "own_user" — only rows written under user_id, which must be set. "own_session" — only rows…
entity_filter list[str] (computed) If non-empty, OR-match against extracted entities — rows whose entities array intersects this list are boosted alongside cosine matches.
category str '' If set, restrict the search to memories assigned to this category (one registered in the namespace).
tags_soft bool False --- Soft metadata ranking (sharpen recall without the coverage loss of a hard filter).
tag_boost float 0.3 Max rank contribution from tag overlap when tags_soft (scaled by fraction of query tags present).
category_soft bool False True turns category from a hard filter into a rank boost of category_boost for its members; False (default) returns members only.
category_boost float 0.3 Rank contribution for category members when category_soft.
confidence_weight float 0.1 Per-request weight on stored confidence in the blended rank score.
prior_cap float \| None None Cap on the discretionary-prior group (confidence + soft tag/category) so it tiebreaks rather than re-ranks.
sensitivity_penalty float \| None None Demotion applied to rows tagged sensitivity:medium\|high.
rrf_adaptive_lexical bool \| None None RRF only: down-weight the lexical leg for code/path-shaped queries.
demote_tags list[str] (computed) Hard-demote: rows whose tags intersect this list are pushed to the bottom of the ranking (large negative term) but NOT dropped — for known-noise markers (e.g. "recurring_false_positive") that still surface.
reinforcement_weight float \| None None Decayed-reinforcement boost overrides (None → server default). weight sizes the boost from reinforcement_count (recall + positive memory.outcome), half_life_days sets how fast it decays since last reinforcement.
recency_pool_weight float \| None None Per-request override of the recency pool re-rank weight applied on a recency-intent query; None = the namespace's retrieval_defaults, else the deployment default (0.0 = off).
reinforcement_half_life_days float \| None None Days for the reinforcement boost to halve since a row was last reinforced; None = the deployment default.
mention_boost float \| None None Pool-consensus mention boost: hits whose entities the rest of the candidate pool also mentions move up by at most prior_cap.
ground_relative_dates bool \| None None Append the row's resolved date to a fact excerpt that still carries a bare relative phrase ("… yesterday (on 2023-11-16)").
reinforcement_half_life_use_scale float \| None None Use-scaled half-life: half_life · (1 + scale · log1p(reinforcement_count − 1)).
key_match bool \| None None Key-match arm override: None → deployment default (LOOM_MEMORY_SEARCH_KEY_MATCH_MODE); True/False forces it for this request.
key_match_mode Literal['always', 'gated', 'off'] \| None None always | gated | off for this request; None → the deployment default (LOOM_MEMORY_SEARCH_KEY_MATCH_MODE).
digest_descent bool \| None None When a session digest ranks in the top-m of the pool, pull that session's own top-k facts in as candidates.
role str '' The calling agent's role in a multi-agent run — planner, executor, critic, or the team's own word.
top_k int 5 At most this many hits to return (capped at 500, and lower by a per-token ceiling reported in top_k_ceiling).
key_axis bool \| None None Key-embedding axis.
bodies Literal['full', 'excerpt', 'none', 'unit', 'source'] 'full' What each hit carries back.
body_budget_tokens int 4000 Cap 32,000: a run trace is ~4,000 tokens whole, and a constraint that cites three earlier steps needs three whole traces plus the base; a 12,000 cap clips the third to 1,200 chars without its result.
source_span_chars int 0 bodies="source": 0 serves the whole linked passage; N serves an N-character window centred on the span the fact was extracted from (memory_belief_sources.char_start/char_end): an evidence capsule at a fraction of the trace-sized window.
paging bool \| None None Paging.
page str '' A next_page handle from an earlier call; serves that list's next slice with no retrieval.
recent_first int 0 Run-scoped recency tier.
anchor_first int 0 Run-scoped anchor tier: the N OLDEST memories of this identity — restricted to run_id when the request carries one — are placed first, ahead of similarity hits, within top_k.
per_speaker_quota int \| None None Per-speaker quota: each speaker in the fused pool is guaranteed up to this many rows of the top_k before the remainder fills by rank, so one voice of a two-party conversation cannot take the whole window.
min_body_chars int 0 Drop hits whose body is shorter than this many characters (0 = off).
graph_seed Literal['', 'query', 'recent', 'both'] '' Where the graph walk starts: "query" = the question's entities and nearest triples (the default); "recent" = the entities of this identity's newest memories (the session's when session_id is set), so what was just talked about reaches the…
diversify Literal['', 'none', 'session'] '' Evidence completion. "session": at most diversify_session_cap hits per source session in the block; the rest of a session's rows yield their slot to the next session in rank order.
diversify_session_cap int \| None None Per-session cap for diversify="session"; None = max(2, top_k // 3).
summary_cap int \| None None At most this many digest rows (source summary / entity_summary) in the final block; None or 0 = unbounded.
chunk_cap_share float \| None None Fact rows first: the largest share of the block that verbatim chunk rows may hold; surplus slots go to the best unreached fact rows.
floor_mode Literal['readmit', 'ids_only', 'drop'] \| None None What happens to hits under the similarity floor when top_k is set.
min_similarity float \| None None The similarity below which a hit is noise for THIS read.
chunk_max_share float \| None None At most this share of the window is verbatim chunks (the chunk cap, per read).
personalization Literal['full', 'none'] 'full' Personalization arm. "full" = the default ranking. "none" drops every user-derived rank term in every mode — preference boost, discretionary prior group, reinforcement — and keeps relevance terms.
observation_date str '' ISO YYYY-MM-DD anchor for resolving relative time in the query; when set the resolved date is appended to the embedded query text.
rerank str '' Per-request reranker override.
retrieval_budget Literal['fast', 'standard', 'deep'] 'standard' Cost posture. "standard" stages cheap recall first, spending expensive LLM/graph/rerank legs only when the planner or fallback gates justify them. "fast" makes no LLM call (as recipe "fast"); "deep" allows upfront expensive recall.
hyde bool True HyDE: embed an LLM-generated hypothetical answer instead of the raw query to close the question/stored-phrasing vocabulary gap.
as_of datetime \| None None As-of replay filter (coalesce(valid_at, created_at) <= as_of), denormalized onto memory_embeddings so vector_search applies it without joining loom.memory.
as_of_basis Literal['valid_time', 'knowledge_time'] 'valid_time' Which time axis as_of replays: "valid_time" (default; what was true as of then) or "knowledge_time" (what loom had learned by then — created_at only, ignoring valid_at; the incremental-ingest knowledge cutoff).
Output field Type Meaning
results list[_MemorySearchHit] The ranked hits, best first; empty when nothing passed the noise floor.
reader_block str render="reader" only: the results as one prompt block ("[date] [key] (type) excerpt", ordered as memory.ask would read them) and the synthesizer's instructions, so an external reader scores what the ask reader scores on the same hits.
reader_instructions str render="reader" only: the synthesizer instructions memory.ask pairs with reader_block; empty otherwise.
bodies_returned int bodies="unit" accounting: whole values served / withheld for budget, and the estimated tokens those bodies added (len/4).
bodies_elided int Hits whose whole value was withheld because body_budget_tokens ran out.
tokens_returned int Estimated tokens (chars / 4) the whole values served under bodies="unit" added.
namespaces_searched list[str] The namespaces in this search's scope: the addressed one first, then the fan-out under namespace_scope=all_mine.
namespaces_skipped list[str] Namespaces of the caller the fan-out did not read because it stops at memory_search_fanout_max_namespaces; address one by name to search it.
candidates_total int Distinct memories in the fused pool before the top_k cut ("showing 10 of 124"); the handle that serves the next slice when the caller asked for paging and there was more; and this page's offset into the parked list (0 for a fresh search).
stale_dropped int Memory hits left out because the Postgres authority no longer serves what they show (always 0 under the ClickHouse authority); a short or empty page may have more behind next_page, so keep paging while it is set.
next_page str Handle to pass as page for the next slice; empty when paging was off or no more candidates remained.
page_offset int This page's offset into the parked list; 0 for a fresh search.
chunk_share float Share of the returned window that is verbatim chunks: the reader for "did chunks crowd out the facts" on the ledger.
embedding_model str The embedding model the query was embedded with.
next str Protocol nudge, set only when the call carried a run_id: how to report which of these hits the agent relied on (run.complete memories_used).
scratchpad str The identity's working-memory blocks, rendered as markdown, when the call named a session_id or run_id and a block exists for that scope.
scratchpad_blocks int How many working-memory blocks scratchpad rendered; 0 = none.
reinforcement str \| None Whether this read's recall credit was written.
graph_seeds int Graph-mode diagnostics, summed across the cross-namespace fan-out. graph_seeds = 1-hop seed entities; graph_neighbors_added = neighbors materialized from entity_edges and fed to rank fusion as graph_overlap.
graph_neighbors_added int Neighbor memories pulled in from entity edges and fused as graph overlap; 0 when graph expansion did not run or found none.
triples_extracted_from_query int Relation-typing diagnostics: (s,v,o) triples pulled from the query, and the deduped count of memory_keys with matching stored triples.
triples_matched int Distinct memories whose stored triples matched a query triple.
set_expansion_phrases int How many instance phrases the set-question expansion ran as extra legs (0 = trigger did not fire or the lever is off).
top_k_ceiling int Abstention signal: True when retrieval can't honestly answer — results empty after the noise floor, or top similarity below LOOM_MEMORY_SEARCH_ABSTAIN_THRESHOLD.
abstain bool True when retrieval cannot honestly answer: no hit survived the noise floor, the top similarity or calibrated confidence was too low, or a named entity is absent.
abstain_reason str Human-readable reason for abstain; empty when it is False.
abstain_code Literal['', 'no_match', 'below_threshold', 'low_calibrated_confidence', 'entity_absent', 'page_expired'] Bounded machine-readable companion to abstain_reason.
degraded list[str] Providers that fell back on this read and what the response did instead (rerank: input_order_kept (ReadTimeout)).
recipe str Named recipe applied (if any) + a terse final scoring posture.
score_explanation str Terse scoring posture: mode, recipe, hyde, rerank and tag filter, then the retrieval plan (mode=rrf, hyde=on; plan=...).
retrieval_confidence float Calibrated confidence in the hit set, 0 to 1, from top similarity, top-two margin, route agreement and result count; 0 on no hits.
retrieval_risk Literal['low', 'medium', 'high', 'abstain'] Risk label from retrieval_confidence: "low" (>= 0.75), "medium" (>= 0.55), "high" (below), "abstain" (below the abstain thresholds).
route_agreement float Share of the retrieval routes that ran (semantic, graph, triples, evidence, extra legs) that also found support, 0 to 1.
calibration_explanation str The inputs behind retrieval_confidence: top_similarity, margin, route_agreement, evidence_support (and constrained=true when set).
fallback_rounds_used int Recursive-fallback cascade accounting (iterative-judge). rounds_used = rounds spent (capped at LOOM_MEMORY_SEARCH_FALLBACK_MAX_ROUNDS); budget_exhausted = a leg was denied by the round/wall-clock cap; elapsed_ms = wall-clock in the cascade.
fallback_budget_exhausted bool True when a fallback leg was denied by the round or wall-clock cap.
fallback_elapsed_ms int Wall-clock milliseconds spent in the fallback cascade.
retrieval_plan str Planner diagnostics: pre-execution shape estimate + best-effort actual work.
retrieval_cost_estimate dict[str, Any] Pre-execution work estimate: budget_class, llm_calls, embedding_calls, vector / lexical / graph / structured queries, candidate_pool, top_k.
retrieval_work dict[str, Any] Work actually done: per-stage counters (e.g. <stage>_added), fallback LLM and vector-query use against their budgets, and llm_calls_total.
pending_extraction int Deferred set_from_messages batches not yet extracted in the searched scope. >0 means "a save is in flight", so an empty or thin result is not evidence of absence; re-ask after the worker runs.
derived_aggregate dict[str, Any] \| None Exact in-ClickHouse aggregate for derived-answer queries (aggregational / temporal_diff) over per-occurrence rows — what the vector planes structurally can't produce.

memory.search_trace

Same shape as memory.search; returns the vector leg's per-candidate scoring breakdown (no fusion, assembly or reranker).

POST /v1/memory.search_trace · MCP loom_memory_search_trace · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str '*' Defaults to "*" = read ALL agents in the accessible namespaces: membership is the read boundary, agent is authorship metadata.
render Literal['', 'reader'] '' render="reader": also return reader_block + reader_instructions, the hits rendered and instructed the way memory.ask reads them.
query str required The natural-language question or keywords to search for; it is embedded for the semantic legs and tokenized for the lexical ones.
memory_type Literal['semantic', 'procedural', 'preference', 'reflection', 'decision'] \| None None None = every searchable type except the hidden-by-default ones (reflection). "reflection" = only process lessons (what worked / failed in past runs), which never surface in an unfiltered search.
tags list[str] (computed) OR-filter on tags (set semantics).
scope Literal['private', 'shared'] 'private' Which rows to read: "private" = per-agent rows (narrowed to agent when it names one), "shared" = rows promoted to the namespace's shared scope.
namespace_scope Literal['this', 'all_mine'] \| None None "this" reads input.namespace only; "all_mine" also the caller's own namespaces, see docs/concepts/identities-and-namespaces.md#reading-several-namespaces.
search_mode Literal['semantic', 'lexical', 'hybrid', 'bm25_hybrid', 'related', 'graph', 'rrf', 'temporal'] 'rrf' Recall fusion mode.
recipe Literal['', 'fast', 'balanced', 'quality', 'verbatim', 'code', 'temporal', 'graph'] '' Named presets over the lower-level knobs.
alpha float 0.5 Lexical weight for search_mode "hybrid" / "bm25_hybrid": the score is alpha * lexical + (1 - alpha) * cosine.
include_top_n_unmatched int 0 When > 0 and a reranker is configured, fetch this many extra candidates by recency to widen the reranker's pool.
include_tags bool False Carry each hit's tags on the response.
include_usage bool False Carry each hit's reliance counters on the response — reinforcement_count (the folded rank signal), used_count (an agent's label: memory.outcome used / run.complete memories_used), used_cited_count (memory.ask's own credit for citing…
include_duplicates bool False A status='duplicate' row (set-time dedup at 0.97 cosine named its canonical in duplicate_of) is not served; its canonical takes the slot, disclosed on the hit as resolved_from.
include_superseded bool False A status='superseded' row (a later write replaced it) is not served unless asked.
include_proposed bool False status='proposed' rows (facts written for trial, accepted or rejected on graded run outcomes) are served only here, when asked.
explain bool \| None None Carry per-hit explanation and score_components.
legs list[Literal['memory', 'sessions']] \| None None The legs the caller can show.
auto_touch bool True Record append-only recall usage for every returned row (powers recall_count / last_recalled_at via loom.memory_usage_*, no read-path mutation of loom.memory).
user_id str \| None None filter by user_id (for preference recall)
run_id str \| None None only rows written under this run; unset or "-" reads every run
session_id str \| None None Filter by session_id.
session_ids list[str] (computed) Restrict to a set of sessions: a caller may pin its own.
exclude_keys list[str] (computed) Keys the caller already holds and does not want served again: dropped from the ranked pool BEFORE the window is assembled, so the window fills from the next-best candidates instead of returning a shorter list.
quality_filter bool False Drop stored rows that are not facts (trajectory steps, session narration, tool tips, code-location trivia, probes, one-moment status — server/memory/domain/fact_quality.py) and restatements of a higher-ranked fact from the pool before the…
read_scope Literal['namespace', 'own_user', 'own_session'] 'namespace' How far this read may reach inside each namespace: "namespace" — every row the token can access (membership is the read boundary; the default). "own_user" — only rows written under user_id, which must be set. "own_session" — only rows…
entity_filter list[str] (computed) If non-empty, OR-match against extracted entities — rows whose entities array intersects this list are boosted alongside cosine matches.
category str '' If set, restrict the search to memories assigned to this category (one registered in the namespace).
tags_soft bool False --- Soft metadata ranking (sharpen recall without the coverage loss of a hard filter).
tag_boost float 0.3 Max rank contribution from tag overlap when tags_soft (scaled by fraction of query tags present).
category_soft bool False True turns category from a hard filter into a rank boost of category_boost for its members; False (default) returns members only.
category_boost float 0.3 Rank contribution for category members when category_soft.
confidence_weight float 0.1 Per-request weight on stored confidence in the blended rank score.
prior_cap float \| None None Cap on the discretionary-prior group (confidence + soft tag/category) so it tiebreaks rather than re-ranks.
sensitivity_penalty float \| None None Demotion applied to rows tagged sensitivity:medium\|high.
rrf_adaptive_lexical bool \| None None RRF only: down-weight the lexical leg for code/path-shaped queries.
demote_tags list[str] (computed) Hard-demote: rows whose tags intersect this list are pushed to the bottom of the ranking (large negative term) but NOT dropped — for known-noise markers (e.g. "recurring_false_positive") that still surface.
reinforcement_weight float \| None None Decayed-reinforcement boost overrides (None → server default). weight sizes the boost from reinforcement_count (recall + positive memory.outcome), half_life_days sets how fast it decays since last reinforcement.
recency_pool_weight float \| None None Per-request override of the recency pool re-rank weight applied on a recency-intent query; None = the namespace's retrieval_defaults, else the deployment default (0.0 = off).
reinforcement_half_life_days float \| None None Days for the reinforcement boost to halve since a row was last reinforced; None = the deployment default.
mention_boost float \| None None Pool-consensus mention boost: hits whose entities the rest of the candidate pool also mentions move up by at most prior_cap.
ground_relative_dates bool \| None None Append the row's resolved date to a fact excerpt that still carries a bare relative phrase ("… yesterday (on 2023-11-16)").
reinforcement_half_life_use_scale float \| None None Use-scaled half-life: half_life · (1 + scale · log1p(reinforcement_count − 1)).
key_match bool \| None None Key-match arm override: None → deployment default (LOOM_MEMORY_SEARCH_KEY_MATCH_MODE); True/False forces it for this request.
key_match_mode Literal['always', 'gated', 'off'] \| None None always | gated | off for this request; None → the deployment default (LOOM_MEMORY_SEARCH_KEY_MATCH_MODE).
digest_descent bool \| None None When a session digest ranks in the top-m of the pool, pull that session's own top-k facts in as candidates.
role str '' The calling agent's role in a multi-agent run — planner, executor, critic, or the team's own word.
top_k int 5 At most this many hits to return (capped at 500, and lower by a per-token ceiling reported in top_k_ceiling).
key_axis bool \| None None Key-embedding axis.
bodies Literal['full', 'excerpt', 'none', 'unit', 'source'] 'full' What each hit carries back.
body_budget_tokens int 4000 Cap 32,000: a run trace is ~4,000 tokens whole, and a constraint that cites three earlier steps needs three whole traces plus the base; a 12,000 cap clips the third to 1,200 chars without its result.
source_span_chars int 0 bodies="source": 0 serves the whole linked passage; N serves an N-character window centred on the span the fact was extracted from (memory_belief_sources.char_start/char_end): an evidence capsule at a fraction of the trace-sized window.
paging bool \| None None Paging.
page str '' A next_page handle from an earlier call; serves that list's next slice with no retrieval.
recent_first int 0 Run-scoped recency tier.
anchor_first int 0 Run-scoped anchor tier: the N OLDEST memories of this identity — restricted to run_id when the request carries one — are placed first, ahead of similarity hits, within top_k.
per_speaker_quota int \| None None Per-speaker quota: each speaker in the fused pool is guaranteed up to this many rows of the top_k before the remainder fills by rank, so one voice of a two-party conversation cannot take the whole window.
min_body_chars int 0 Drop hits whose body is shorter than this many characters (0 = off).
graph_seed Literal['', 'query', 'recent', 'both'] '' Where the graph walk starts: "query" = the question's entities and nearest triples (the default); "recent" = the entities of this identity's newest memories (the session's when session_id is set), so what was just talked about reaches the…
diversify Literal['', 'none', 'session'] '' Evidence completion. "session": at most diversify_session_cap hits per source session in the block; the rest of a session's rows yield their slot to the next session in rank order.
diversify_session_cap int \| None None Per-session cap for diversify="session"; None = max(2, top_k // 3).
summary_cap int \| None None At most this many digest rows (source summary / entity_summary) in the final block; None or 0 = unbounded.
chunk_cap_share float \| None None Fact rows first: the largest share of the block that verbatim chunk rows may hold; surplus slots go to the best unreached fact rows.
floor_mode Literal['readmit', 'ids_only', 'drop'] \| None None What happens to hits under the similarity floor when top_k is set.
min_similarity float \| None None The similarity below which a hit is noise for THIS read.
chunk_max_share float \| None None At most this share of the window is verbatim chunks (the chunk cap, per read).
personalization Literal['full', 'none'] 'full' Personalization arm. "full" = the default ranking. "none" drops every user-derived rank term in every mode — preference boost, discretionary prior group, reinforcement — and keeps relevance terms.
observation_date str '' ISO YYYY-MM-DD anchor for resolving relative time in the query; when set the resolved date is appended to the embedded query text.
rerank str '' Per-request reranker override.
retrieval_budget Literal['fast', 'standard', 'deep'] 'standard' Cost posture. "standard" stages cheap recall first, spending expensive LLM/graph/rerank legs only when the planner or fallback gates justify them. "fast" makes no LLM call (as recipe "fast"); "deep" allows upfront expensive recall.
hyde bool True HyDE: embed an LLM-generated hypothetical answer instead of the raw query to close the question/stored-phrasing vocabulary gap.
as_of datetime \| None None As-of replay filter (coalesce(valid_at, created_at) <= as_of), denormalized onto memory_embeddings so vector_search applies it without joining loom.memory.
as_of_basis Literal['valid_time', 'knowledge_time'] 'valid_time' Which time axis as_of replays: "valid_time" (default; what was true as of then) or "knowledge_time" (what loom had learned by then — created_at only, ignoring valid_at; the incremental-ingest knowledge cutoff).
Output field Type Meaning
results list[_SearchTraceHit] Every candidate the fact leg scored that is still served (all of them under an as_of replay), in the leg's rank order, with its breakdown.
embedding_model str The embedding model the query was embedded with.
entity_filter list[str] The request's entity_filter, lowercased as it was matched.
query_spellings dict[str, str] Query words embedded in the corpus's spelling instead of as typed (memory_search_query_spelling_max_words); {} when none was.
query_spellings_skipped bool True when the query was embedded as typed because the spelling lookup did not answer (failed, timed out, namespace backing off) or the respelled text's embed failed.
stale_dropped int Under the Postgres authority: candidates left out because the authority no longer serves what they show, as memory.search's stale_dropped. 0 under ClickHouse and under an as_of replay.

memory.search_trace_log

Read loom.search_trace_log for a namespace (requires the telemetry flag).

POST /v1/memory.search_trace_log · MCP loom_memory_search_trace_log · role viewer · read

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
since_hours int 24 Look-back window in hours, 1 to 8760 (one year).
agent str '' The writing or reading agent; '-' or '*' means any agent where the op allows it.
run_id str '' Only searches made under this run; "" or "-" = every run.
limit int 100 At most this many rows, newest first; capped at 1000.
Output field Type Meaning
rows list[_SearchTraceLogRow] Recorded searches, newest first.

memory.set

Save a fact to Loom — shared across every AI tool the user runs; native memory is the Claude-only scratchpad.

POST /v1/memory.set · MCP loom_memory_set · role writer · idempotent

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
memory_key str required The key to write; a write to a key that already holds a row adds a new version.
memory_type str 'semantic' Coarse classification axis (drives procedural memory + tag_registry policy lookups).
value dict[str, Any] \| list[Any] \| str \| int \| float \| bool required The fact itself: plain text, or a JSON object, list or scalar.
tags list[str] (computed) Flexible classification, filterable at recall via MemorySearchInput.tags; per-tag retention/dedup/conflict policy via NamespaceRegisterInput.tag_registry.
entities list[str] (computed) Explicit entities for a point write.
relations list[tuple[str, str, str]] (computed) The (subject, relation, object) facts the value states, written to the fact graph and linked to the row as a memory.bulk_set entry's are: a single-valued fact closes the older one it replaces and, under newest_wins, retires the rows…
files list[str] \| None None The repo files this memory is about (repo-relative path or bare filename); memory.for_file serves it before an edit of them.
confidence float 1.0 The writer's own confidence in the fact, 0..1 (1.0 = certain).
source_trace str '' Provenance tag of the write (conversation:<id>, curated_doc://…); a known prefix decides source_class (conversation: → extracted), and one starting with memory.delete_version: is refused.
source_class str \| None None Declare that this content came from outside (a tool result, a page, a document): external, or imported.
source_resource_id str '' Provenance pointer to the primary source this fact was cited from — a file path, document id, or URL.
ttl_seconds int \| None None Expire the row this many seconds from now.
clear_expiry bool False Force expires_at=NULL, ignoring per-type/namespace retention defaults.
on_expire Literal['', 'tombstone', 'demote', 'anonymize'] '' What happens when expires_at passes: '' keeps the row's current action (tombstone for a new row); 'demote' clears the expiry and scales confidence down; 'anonymize' replaces the value with a marker and drops the embeddings, keeping the…
expire_reason str '' Why the expiry is set (at most 512 chars, not starting with blacklist.sweep:); '' keeps the row's current reason.
valid_at datetime \| None None Real-world time the fact became true (distinct from created_at and from temporal_anchor).
valid_to datetime \| None None Closing bound of the fact's stated validity ("until 2024"); NULL = open interval.
pinned bool False True when written via memory.pin (explicit-save)
classify bool False Call the namespace's classify_endpoint to derive memory_type from value.
if_match_sha256 str '' Optimistic-concurrency precondition: 409 PRECONDITION_FAILED if the live row's SHA-256 doesn't match.
char_limit int \| None None Server-enforced char cap on the stringified value (422 on exceed).
sensitivity Literal['', 'low', 'medium', 'high'] '' Sensitivity tier. 'high' = never embedded, searched or in a recency pack; read by key only, audited. '' = detect from the value: credentials, card numbers, SSNs, PINs high; email, phone, IBAN medium.
dry_run bool False Run the conflict check and return its verdict, peers and next WITHOUT writing — no loom.memory row, no embedding row, no audit hash, no usage event, no supersession.
proposed bool False Write the fact as a PROPOSAL (status='proposed'): not served by default, served to a read that passes include_proposed, and accepted or rejected on the graded outcomes of the runs that used it (run.grade, memory.proposals…
Output field Type Meaning
version int nanosecond version assigned by the server
pinned bool True when the stored row is pinned: a memory.pin, or a restatement of a pinned row.
memory_id str UUID assigned to this insert; usable with memory.get_by_id
content_sha256 str SHA-256 of the just-written value; usable as if_match_sha256 on next write.
status str The written row's lifecycle status (active, duplicate, proposed); "" on a write that stored no row (merge, dry run).
duplicate_of str Set when this write was tagged status='duplicate': memory_id of the canonical semantically-identical row.
namespace str Echoed so a tool-call response carries citable factual data.
memory_key str The key the request named; on a merge see merged_into_key.
merged_into_key str On a merge the store already held this content under ANOTHER key; nothing was written at memory_key, so a get/lock/update of it finds no row.
degraded list[str] Providers that fell back on this write and what the response did instead (classify: caller_type_kept (ReadTimeout)).
retyped_from str Set when the declared memory_type did not fit the value and the row was stored under another type (currently: 'procedural' on a value that is not a procedure body → 'semantic', tagged retyped_from:procedural).
conflict_verdict str What the write-time conflict check decided about THIS write — clear | merge | duplicate | conflict | refines | rejected (the last only on a dry run; a real write answers 409).
peers list[MemorySetPeer] The rows the verdict was reached against (closest first, at most five): the key is the handle for memory.update, the excerpt what the store holds.
next str A short machine hint naming the op to reach for: memory.update(key) on a conflict, memory.pin on a merge the store has seen before, "" otherwise.
noop bool True when this call wrote nothing: a merge (the store already held the fact; its row was reinforced instead) or a dry run.
dry_run bool Echo of the request's dry_run so a tool-call transcript is unambiguous.
advisories list[str] One line per thing the server noticed about the VALUE that the agent should fix on its side — currently only "base64-encoded".
chunks_written int Verbatim chunk rows this write added to the chunk plane for a long searchable value (0 for a short value, a non-searchable type, a merge or a dry run).

memory.set_from_messages

Run LLM extraction on messages and write the extracted facts.

POST /v1/memory.set_from_messages · MCP loom_memory_set_from_messages · role writer · write

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
messages list[_Message] required 400, not 100: an unattended session or a grouped batch of sessions is longer than a chat turn, and topic segmentation exists to split exactly that into topic-sized extraction calls.
extra_tags list[str] (computed) Applied to every extracted fact, merged with the LLM-extracted tags.
recalled_keys list[str] (computed) The memory keys the session's Loom tools SERVED (the plugin's hooks stash them).
session_values list[str] (computed) The text the session's own Loom tool calls carried — the values they wrote, the queries they asked, the results they got (the plugin reads them from the transcript at SessionEnd).
max_tokens int 4096 Ceiling on the extraction call; default gives headroom for ~40+ facts.
judge_extraction bool False Optional second-pass extractor judge (adds an LLM call); off by default.
record_schema dict[str, str \| list[str]] \| None None Typed extraction.
memory_key str \| None None required iff record_schema is set
observation_date str '' ISO YYYY-MM-DD anchor for relative time references ("yesterday", "last week").
llm_cache bool \| None None Per-call pin of the extractor's exact-prompt response cache.
detect_updates bool \| None None Value-update supersession (memory.app.value_updates): after the write, link each new memory that restates an existing one with a different value token, marking the older side superseded.
last_messages list[_Message] (computed) Prior turns from the SAME session not being extracted this call — lets the extractor resolve pronouns/follow-ups/cross-turn references without re-extracting earlier facts.
extraction_instructions str '' Per-request extraction-prompt suffix for workload-specific tuning.
coverage_sweep bool \| None None The coverage sweep (turn- and sentence-level second reading) per call — None = the namespace, then the density gates.
agent_learnings bool \| None None None follows memory_extract_agent_learnings (default off); false skips filing the assistant's rules for this call, and true while the setting is off is named in ignored_args.
pipeline_steps list[dict[str, Any]] (computed) Per-call workflow override.
valid_at datetime \| None None Fallback valid_at for facts the extractor didn't anchor itself.
defer bool False Capture now, extract later.
Output field Type Meaning
written list[_BulkSetResult] Key and version of each extracted fact written; empty on a deferred call.
refused list[_RefusedFact] The extracted facts the write refused (a blacklisted or unchecked value, a pinned or locked key, or a pinned row close by), with the verdict; not in written or the counts.
tokens_in int Prompt tokens of the extraction LLM calls.
tokens_out int Completion tokens of the extraction LLM calls.
llm_provider str The LLM provider that ran the extraction; '' when deferred.
llm_model str The model that ran the extraction; '' when deferred.
segments int Extraction calls made: 1, or the number of topic segments.
foresight int Facts written as time-bounded foresight rows.
forgotten int Memories retired because the user asked to forget them, and their keys.
forgotten_keys list[str] The keys of the memories those forget requests retired.
forget_requests int Requests to forget read from the user's own sentences and resolved by content (a window's latest 50); with forgotten it says how many found a row to retire.
about_other int Rows written as another person's (about:other), whether the extractor said so or the sentence's subject did.
premise_facts int The user's facts read from the assistant's premises ("since you enjoy X…"), added to the batch (at most 50 a window).
learning_candidates int Rules the assistant stated about itself that wrote to the learnings queue: a new candidate or this session's support of one (a session restating a rule it already backs counts 0).
ignored_args list[str] Request fields the server did not apply: unknown keys, and agent_learnings=true while the deployment setting memory_extract_agent_learnings is off.
segmenter str Which planner cut them: whole | size.
cached_tokens_in int Subset of tokens_in the provider served from its prompt cache (billed at a discount).
ontology_warnings list[str] Extracted entity types / relation verbs outside the namespace's ontology (loom.namespace_ontology).
evidence_ids list[str] Evidence-first provenance: set when raw source evidence was persisted before extraction and linked to the written memories.
belief_source_links int Links written from the new memories to the stored source evidence rows.
pipeline_errors list[str] Fail-open pipeline steps that logged-and-skipped (operator hook unreachable, fetch_existing failed, unknown step id, ...).
value_updates_linked int Older memories this ingest marked superseded by a value update (memory.app.value_updates; 0 when detection is off).
stubbed_fields list[str] Schema mode only: record_schema fields the extractor could not validate after its retries and wrote as null (never a fabricated value).
degraded list[str] Schema mode only: what the response skipped and why, e.g. the memory_records projection for a caller bounded to one user.
deferred bool defer=true: nothing extracted yet; written is empty and queue_id is the handle memory.extraction_status_mine (and memory.extraction_status) report on.
queue_id str The queued batch's id for memory.extraction_status_mine and memory.extraction_status; '' when inline.
deferred_by str "" (inline) | "flag" (the caller passed defer=true) | "allowlist" (the deployment's extraction_defer_agents chose for the caller)
stage_ms dict[str, float] Wall time per stage of the ingest: handler stages (evidence_write, pipeline, projections, ontology, total), pipeline steps (step.<id>) and bulk-write phases (bulk.<phase>).
observation_date_source str Where the observation date the facts were anchored to came from: "caller" | "request_time" | "" (none — undated rows).
under_operator_policy bool True when the extraction ran under an operator's extraction policy; false on a deferred request.
extract_prompt_keys list[str] The sha256 keys of the rendered extractor prompts (one per segment; the exact-prompt cache's own key, no text) and the cache's hit / miss counts over this extraction's calls (0 / 0 = cache off).
llm_cache_hits int Extraction calls answered from the exact-prompt response cache.
llm_cache_misses int Extraction calls the cache could not answer.

memory.stack

Compose a token-budgeted recall bundle for agent wake-up.

POST /v1/memory.stack · MCP loom_memory_stack · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
token_budget int 4000 Estimated tokens (chars / 4) all sections share; an entry that would overflow it ends its section.
pinned_limit int 10 At most this many pinned memories, newest version first (0 skips the section).
recent_limit int 20 At most this many recalled, unpinned memories, most recalled first (0 skips the section).
include_knowledge bool False True adds a knowledge section searched on seed_query (or the recent-recall excerpt).
knowledge_top_k int 3 Knowledge hits to request when include_knowledge is set; 0 skips the section.
seed_query str '' Drives the procedure.match query and the knowledge-search query.
procedure_limit int 3 Procedures to match on seed_query; 0, or no seed_query, skips the section.
Output field Type Meaning
entries list[_StackEntry] Profile, pinned, recent_recall, procedure, knowledge entries, in that section order.
tokens_used int Estimated tokens the returned entries use (chars / 4 each).
tokens_budget int The token_budget the bundle was trimmed to, echoed.
truncated bool true when at least one section was trimmed to fit
section_counts dict[str, int] how many entries per section

memory.statuses

The current status of some memories, by id: which automatic retirements still stand (the Review queue's Auto-resolved tab reads it).

POST /v1/memory.statuses · MCP loom_memory_statuses · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
memory_ids list[str] required Ids this namespace does not hold are left out, including one replaced by a racing first write of the same key.
Output field Type Meaning
rows list[MemoryStatus] One entry per id the namespace holds inside the read scope.

memory.sweep_expired

Run one expiry-sweep pass now — the same pass the memory_expiry_sweep worker runs on its interval, scoped to one namespace.

POST /v1/memory.sweep_expired · MCP loom_memory_sweep_expired · role admin · idempotent · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace swept; required, so the gate checks the caller's admin role there (the worker sweeps every namespace).
limit int 500 At most this many expired rows handled in the pass.
dry_run bool False True counts and samples what the pass would do, writing nothing.
Output field Type Meaning
scanned int Expired live rows the pass read.
tombstoned int Rows tombstoned (or that would be, under dry_run).
demoted int Rows demoted: expiry cleared, confidence scaled down (or would be, under dry_run).
anonymized int Rows anonymized: value replaced by a marker, embeddings tombstoned, the shell row kept (or would be, under dry_run).
locked_skipped int Rows left as they are because a lock refuses their post-action; reads still hide them.
moved_skipped int Rows left for the next pass because their head moved on since the read (renewed, rewritten or deleted); 0 under the ClickHouse authority.
dry_run bool Echo of the request's dry_run.
sample list[dict[str, Any]] Up to 20 handled rows: memory_key, namespace, agent, action, expired_at.
failures list[str] Errors of the pass (scan:, insert:, mirror:); empty on a clean run.

memory.timeline

Return memories about an entity (or matching a tag filter) in strict temporal order — newest first, with optional since/until windows.

POST /v1/memory.timeline · MCP loom_memory_timeline · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
entity str '' Only memories tagged with this entity (matched lowercased); empty = no filter.
tags list[str] (computed) Only memories carrying at least one of these tags; empty = no filter.
memory_key_prefix str '' Only memories whose key starts with this; empty = no filter.
since datetime \| None None Inclusive lower bound on coalesce(valid_at, created_at); None = unbounded.
until datetime \| None None Inclusive upper bound on coalesce(valid_at, created_at); None = unbounded.
top_k int 50 At most this many events; total_matching counts them all.
order Literal['desc', 'asc'] 'desc' desc returns newest first, asc oldest first.
Output field Type Meaning
events list[_MemoryTimelineEntry] Matching private memories in time order, each with a content excerpt.
total_matching int Total matches before top_k truncation, so callers can detect "there's more".

memory.touch

Change only a memory's expiry: set a new TTL (ttl_seconds) or clear it (clear_expiry), for one key or up to 1,000 (memory_keys), without rewriting its value, entities, conflict state or…

POST /v1/memory.touch · MCP loom_memory_touch · role writer · write · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
memory_key str '' One key to touch; send it or memory_keys, not both.
memory_keys list[str] (computed) Many keys of the same identity in one call — one read, one loom.memory insert, one batched embedding re-emit.
ttl_seconds int \| None None New expiry this many seconds from now; send it or clear_expiry: true, not both.
clear_expiry bool False "never expire" — expires_at becomes NULL.
on_expire Literal['', 'tombstone', 'demote', 'anonymize'] '' Optionally re-declare the post-action alongside the new expiry.
expire_reason str '' Why the new expiry is set (at most 512 chars, not starting with blacklist.sweep:); '' keeps the row's reason.
Output field Type Meaning
touched bool single key: False when it doesn't exist; many: any touched
version int \| None Single key: the new version (None when not touched).
expires_at datetime \| None The new expiry of the touched rows; None when cleared or none touched.
results list[MemoryTouchResult] With memory_keys, one row per asked key.
touched_count int With memory_keys, how many keys were touched; 0 for one key.

memory.unlock

Lift a lock set by memory.lock (admin).

POST /v1/memory.unlock · MCP loom_memory_unlock · role admin · idempotent · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
memory_key str required The key to unlock, under this exact identity.
unlocked_by str '' Actor reported when the server runs without auth; under auth the token subject is reported instead.
Output field Type Meaning
unlocked bool False when the key doesn't exist or carried no lock
reason str "not_found" | "not_locked" when unlocked is False
memory_id str Stable id of the row; '' when not found.
version int \| None Nanosecond version of the unlocked row; None when nothing was written.
previous_mode str The lock mode lifted (a lapsed lock's too); '' when none.
unlocked_by str Who lifted the lock (human:<subject> / process:<subject> under auth); '' when nothing was unlocked.

memory.unpin

Remove the write-time pin from one live memory.

POST /v1/memory.unpin · MCP loom_memory_unpin · role admin · idempotent · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
memory_key str required The key to unpin: tried under this identity, then the same user's row under any agent, matching memory.delete's key resolution.
Output field Type Meaning
unpinned bool False when the key is absent or already unpinned.
reason str "not_found" | "not_pinned" on an idempotent no-op.
memory_id str
version int \| None

memory.update

Key-addressed update: the fact changed or got more specific.

POST /v1/memory.update · MCP loom_memory_update · role writer · write

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
memory_key str required The key to update, resolved across the namespace unless an identity field below narrows it.
value dict[str, Any] \| list[Any] \| str \| int \| float \| bool \| None None None = keep the current value (a tags/concepts-only rewrite).
agent str '' Narrows the key to rows written by this agent; "", "-" or "*" = any agent.
user_id str '' Narrows the key to this user's rows; "" or "-" = any user.
session_id str '' Narrows the key to this session's rows; "" or "-" = any session.
run_id str '' The run this revision is made under: stamped on the new version's row so the run ledger and the acted fold see that this run rewrote the key.
tags list[str] \| None None Replace the row's tags (lowercased); None keeps them, [] clears them.
concepts list[str] \| None None Replace the row's code concepts (symbol, library and error names); None keeps them.
confidence float \| None None New confidence for the written version (None = keep the row's).
reason str '' Why the fact changed — lands in the audit row (audit=True), not on the memory.
if_match_sha256 str '' Content hash the live row must still carry, else 409 PRECONDITION_FAILED; '' skips the check.
char_limit int \| None None Char cap on the new value (422 on exceed); None = no cap on the new version, so pass the row's cap back to keep it.
revive bool False Allow updating an expired row (else 409 EXPIRED); the update clears its expiry.
relations list[tuple[str, str, str]] (computed) The facts the new value states, as MemorySetInput.relations; [] states none.
Output field Type Meaning
updated bool True when a new version was written; False with reason otherwise.
memory_id str Stable id of the updated row; '' when not found.
memory_key str The row's key (the asked key when not found).
new_version int Nanosecond version of the written row; 0 when nothing was written.
content_sha256 str SHA-256 of the new value; send it as if_match_sha256 on the next write.
reason str "" on success; "not_found" when no live row carries the key.
candidates list[MemoryUpdateCandidate] Filled only when the key resolved to several rows (also in the 409 detail).
conflict_verdict str See MemoryUpdateByIdOutput.
conflict_peers int How many existing rows the conflict check matched the new value to.
duplicate_of str memory_id of the row the new value restates, when the check found one.

memory.update_by_id

Update a memory by UUID with an optimistic concurrency check.

POST /v1/memory.update_by_id · MCP loom_memory_update_by_id · role writer · idempotent

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
memory_id str required The row's stable id (from memory.set or a read), org-wide.
value dict[str, Any] \| list[Any] \| str \| int \| float \| bool \| None None None = keep the current value (a tags/concepts-only rewrite).
tags list[str] \| None None Replace the row's tags / concept keys (None = keep).
concepts list[str] \| None None Replace the row's code concepts (symbol, library and error names); None keeps them.
if_match_sha256 str '' No-clobber precondition; same shape as MemorySetInput.if_match_sha256.
char_limit int \| None None Char cap, same as MemorySetInput.
revive bool False An expired row answers 409 EXPIRED unless the caller says revive=True, which also clears the expiry.
relations list[tuple[str, str, str]] (computed) The facts the new value states, as MemorySetInput.relations; [] states none.
Output field Type Meaning
updated bool False when memory_id has no live row in a namespace the caller can write, or when the caller can write more than one namespace holding a live row of it (a restored namespace keeps the ids; use memory.update by key there).
new_version int Nanosecond version of the written row; 0 when nothing was written.
content_sha256 str SHA-256 of the just-written value
conflict_verdict str The semantic conflict verdict on the new value, as memory.set computes it — "" when the check did not run (flag off or a non-searchable type), else clear | duplicate | merge | conflict | refines.
conflict_peers int How many existing rows the conflict check matched the new value to.
duplicate_of str Set when the new value restates or near-duplicates another row: that row's memory_id (mirrors MemorySetOutput.duplicate_of).

memory.verify

Attest to (or revoke the attestation on) one memory.

POST /v1/memory.verify · MCP loom_memory_verify · role writer · idempotent · audited

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
memory_id str '' The row to attest, by id; '' = address it by namespace + memory_key.
namespace str '' The namespace the op reads or writes.
agent str '-' The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str '-' The end user the row belongs to; '-' for none.
session_id str '-' The session or conversation the row belongs to; '-' for none.
memory_key str '' With namespace and the writer identity, the row to attest when no memory_id is sent.
revoke bool False True clears the attestation instead of making one.
actor str '' Attester recorded when the server runs without auth (human: / process: + subject); under auth the token decides.
Output field Type Meaning
found bool False when the row does not exist or the caller cannot write its namespace; by memory_id, also when the caller can write more than one live namespace holding it (a restored namespace keeps the ids; pass namespace + memory_key there).
changed bool False when the call was a no-op (already attested by this actor and unchanged, or revoking a row with no attestation).
memory_id str Stable id of the attested row; '' when not found.
memory_key str The row's key; '' when not found.
namespace str The namespace the op reads or writes.
verified_by str The attester now on the row; '' after a revoke.
verified_at datetime \| None When the attestation was made; None after a revoke.
verification str State against the live content hash: human | process | stale | none.
version int Version of the row after the call (the new row's when changed).
content_sha256 str Canonical hash the attestation is bound to (== the row's live hash).