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. |
memory.backlinks
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: |
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. |
memory.link
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: |
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). |