Repository navigation
New Feature
- The reference extractor reads Parquet files: each file becomes a table whose rows are searchable passages cited by row, and a typed dataset. (#269)
knowledge.querycalculates over the datasets ingested files become: a typed plan of filters, joins, groups and aggregates (or the rows themselves) runs over every row of the live snapshots, and the result carries a receipt naming each snapshot it read. (#278)knowledge.synthesizetakesdatasets: the model proposes aknowledge.queryplan from their schemas, the server checks and runs it over every row, and the answer cites the result as[knowledge.query#1]; when the task is ambiguous the answer abstains with the question to ask. (#279)- The TypeScript SDK sends a file for the server's extractor to parse:
loom.knowledge.uploadposts its bytes,loom.knowledge.waitForIngestfollows the ingest job until it ends, andloom.knowledge.ingestFiledoes both aroundknowledge.ingest, from Node or a browser (Blob,File). (#281) knowledge.formatslists every document format an ingest reads, the file extensions and media types that select each, and whether a configured extractor serves it, with the upload's size limit and the extensions it refuses, so a client can tell a person before sending a file that it would not be read. (#282)loom knowledge migrate-originalsmoves the document originals still stored in ClickHouse to the S3-compatible bucket. Each one is hash-verified before and after the copy, and only then switched to the object. (#285)- The reference extractor reads scanned PDF pages, and pictures that cover a fifth of a page or slide, with OCR: a scan's headings, tables and text are cited by page like any page's, and a diagram's labels are searchable. Without OCR (
LOOM_EXTRACTOR_OCR=off, or an image built withOCR=off) such a page or slide is reported not complete instead of read as if it held nothing. (#298) - The dashboard can erase or export everything Loom holds for a namespace or a user (Activity → Erasures, with each erasure's status and what it left behind), show which tokens read how much of a namespace and of one user's memory (Activity → Exposure), and forget every memory one agent wrote (Agents). (#346)
- A deployment can keep live memory in Postgres (
LOOM_STORAGE_AUTHORITY=postgres,LOOM_PG_DSN): point reads, compare-and-set and the conflict check's view of concurrent writes come from a Postgres transaction, while vectors, search and history stay in ClickHouse. ClickHouse alone stays the default. A deployment chooses the authority at its first boot, on an empty ClickHouse; one whose ClickHouse already holds memory refuses to start withLOOM_STORAGE_AUTHORITY=postgres, so it leaves the setting unset. (#158) - The Helm chart takes a
postgresblock that runs Loom with an external Postgres as the memory authority over verify-full TLS, andvalues-clickhouse-cloud.yamlwith a guide sets up ClickHouse Cloud plus ClickHouse Managed Postgres. (#467) - Under the Postgres authority,
loom reconcile --restored clickhouse|postgres --point <time>repairs a deployment after one of its stores is restored from a backup: the erasures and redactions the other store records past the restore point run again, and after a ClickHouse restore every revision and knowledge document committed since is published again, so nothing erased or redacted comes back. Knowledge chunks live in ClickHouse alone: the command names each source to re-chunk withloom rechunk(past 20 in a namespace, the namespace). (#460) - Under the Postgres storage authority,
namespace.audit,namespace.validate,namespace.tune,memory.inspectandknowledge.inspect's decision counts and memory findings read memory once ClickHouse holds every write the namespace committed, and say so inskippedwhen it does not; a finding cites a row only while the authority still serves that version. (#391) - Under the Postgres storage authority,
knowledge.inspectsays inskippedhow many document commits have not finished publishing to ClickHouse (owed and parked). It lists a document only whileknowledge.getserves it at the listed version, solisted_completeis false whenever the document sections may be short. (#439) - Under the Postgres storage authority, knowledge ingestion jobs are kept in Postgres. A job's status and cancel read what was last saved, and a run that lost its job to another worker saves nothing more. (#447)
- Under the Postgres authority,
knowledge.inspectsays how many ingestion jobs ClickHouse holds at an older version than Postgres, so the job counts and listed jobs are not read as current when they lag. (#466) - Under the Postgres storage authority, the knowledge dataset catalog (which snapshot serves each dataset, and its schema approval) is kept in Postgres.
knowledge.dataset_approvecommits only while the document and snapshot it reviewed still serve. (#465) - Under the Postgres authority, Loom reports how long a search waits for earlier writes to reach ClickHouse, how long a revision takes to become searchable, how many deliveries are owed or parked, and how many ClickHouse inserts its publishers make.
GET /v1/health?include=postgresgains the same backlog, and the monitoring guide states the pipeline's SLOs and alerts. (#469) - Under
LOOM_STORAGE_AUTHORITY=postgreswithLOOM_PG_PAYLOAD_CLEAR_ENABLED, a memory revision keeps only its metadata in Postgres once every usable ClickHouse backup holds it. That happens past a horizon that is never shorter thanLOOM_CH_BACKUP_INTERVAL_MINUTESplus 60. Before restoring a ClickHouse backup, turn the switch off; the steps are in backup and recovery. (#457) - Under
LOOM_STORAGE_AUTHORITY=postgres, amemory.setof a key with no live head answers once Postgres commits, and each process publishes its commits to ClickHouse in shared flushes, one insert per table and month, on average one flush everyLOOM_PG_PUBLISH_INTERVAL_MS(250 ms) with bursts of up to a second's worth, soloom.memoryandloom.memory_embeddingsget parts at that rate. (#433) - Under the Postgres storage authority, knowledge documents have their current version in Postgres. Two concurrent writes of one document cannot both publish or both take the same revision: the later one answers 409
PRECONDITION_FAILED, and a parked document delivery is counted inGET /v1/health?include=postgres(postgres.parked_knowledge_deliveries). Under that authority a namespace'sretention_daysdoes not expire knowledge documents or their chunks, and the server refuses to start withLOOM_RETENTION_DOCUMENTS_DAYSset. (#405) - Under the Postgres storage authority,
knowledge.search,knowledge.outline,knowledge.synthesizeandmemory.stackserve only the chunks of each document's current version (a document written before its namespace had heads keeps serving every version ClickHouse holds): an older version still being retired, a deleted document whose delete is not yet published, and a write that has not committed are left out, andknowledge.searchcounts them in a newstale_droppedfield. Under that authorityknowledge.addandknowledge.bulk_addretire a source's earlier chunk versions, asknowledge.upsertdoes. (#441) - Under
LOOM_STORAGE_AUTHORITY=postgres, amemory.bulk_setthat writes one key twice does not hold the namespace's searches at their barrier, and a bulk write or import of a value the same identity wrote a moment ago is held by it as an exact duplicate. (#446) - Under the Postgres authority, the delivery worker decides a claimed search document from a durable read whenever the key's head is not the claimed revision, a deleted decision's record retirement reads its writers durably, and a knowledge job whose projection lags is sent again only from a durable read, so a failover never leaves ClickHouse with a row, or without one, because of a commit the new primary does not have. (#456)
- The Review queue settles duplicate memories and documents (Duplicates tab, with the namespace-wide repairs), accepts or rejects facts on trial (Proposals tab), and suggests a decision for the conflict on screen. (#359)
- Health shows the namespace's memory report, its search gates and its waiting extractions; a namespace admin applies expiry, re-embeds, removes an extractor's facts, rolls back, snapshots and restores there, and an org admin retries or dismisses projection failures. (#395)
- The memory drawer can lock and unlock a memory, mark it as wrong, share it with every agent in the namespace, record whether it helped, redact a past version, and show the source turns it came from and the typed links into and out of it. Memory history rows carry
version_text, the version as an exact decimal string. (#351) - Guardrails can delete the stored memories a pattern already matches, and an org admin sets the org's and a namespace's extraction rules and switches the org's model calls off or on there; Settings → Memory lists, adds and removes the namespace's memory webhooks and shows their deliveries. (#371)
- The Library stops an ingestion while it runs, shows the typed tables a document's file became and approves their column types, and moves a collection under another; a repository's document source opens its settings, last sync and the documents it wrote. (#396)
knowledge.restoreundoes aknowledge.deleteorknowledge.bulk_delete: the document's last version and its chunks are searchable again. It never brings back what an erasure retired, and it says whether the stored original could be kept and, when it restores nothing, why. (#309)- Settings has a Structure tab: a namespace's record schemas and their records, its profile schema, its graph ontology and its categories, which an admin registers, approves and edits there and a writer adds categories to. (#400)
- The dashboard renames and deletes conversation threads, grades a closed run, forgets a session brief, shows a namespace guide's history, badges a namespace someone just shared, shows the coding agents' model spend, and re-walks a repository branch from a commit. (#402)
insights.query(experimental) runs one read-only SQL statement over an allowlisted set of Loom tables, held to the token's org and namespaces by ClickHouse itself, andinsights.catalogdescribes what it can read. A token withvieweron the namespaces can call both, and both join the assistant toolset. (#397)- With
LOOM_ASSISTANT_LIBRECHAT_URLset, Loom runs LibreChat v0.8.8 headless beside it (the composeassistantprofile, orassistant.enabledin the chart) and seeds one assistant agent at boot, whose tools are the assistant toolset and which recommends how a namespace should write its memories, grounded in the audit, the mentor and Loom's guides. (#418) POST /v1/assistant/chat/completionsasks the Loom assistant a question about a namespace as yourself, streaming or whole in the chat-completions format, andassistant.ask(over HTTP, MCP asloom_assistant_askby name, and the SDKs' generatedops.assistant.ask/loom.assistant.ask) answers one question whole; each answer reads under a token delegated from yours and spends from the namespace's daily model cost cap. (#419)- Each Loom assistant answer comes back with its citations checked: a memory key the answer's own tool calls read, a document section Loom holds and a
sqlblock the answer's owninsights.queryran are each marked verified or not, with the reason and the key, source or path it names (ref), in a lastloom.citationsevent of the stream and in acitationsfield of a whole answer and ofassistant.ask. (#430) - The dashboard's Assistant page has a chat with the Loom assistant about the namespace, under your own access: the answer streams in, and under it its Sources list each citation as Loom checked it, a verified memory or document opening where the dashboard shows it, and an answer with no verified citation marked unsupported. (#444)
- The Python and TypeScript SDKs stream a Loom assistant answer:
agent.assistant.stream(...)andloom.assistant.stream({...})yield its text as it is written, then the answer with its run id, token usage and the citations Loom checked;agent.assistant.ask(...)returns it whole, andSyncAgentiterates the stream blocking. (#443) - A tenant can read its memories, memory history, conversations and request ledger as SQL through the read-only
loom.sql_v1_*views. The ClickHouse user that reads them is made by an operator withloom sql-access grant, scoped to an org and its namespaces. (#450) erasure.statusanderasure.requestreturn the erasure's receipt:census, every substrate the final residue census counted with its count (zeros included), andpurge, whether the saga deleted or tombstoned. The retention and erasure page shows how to re-count the memory and conversation tables through the SQL surface. (#462)- The assistant can read Loom's own documentation, as the running build ships it:
help.searchfinds the sections that answer a question andhelp.getreturns a page or one section. Both are read-only and on the assistant toolset. (#488) - A collection is created from a title:
collection.createwithderive_namederives itscol-…name, and the Library shows titles. A name already used answersCOLLECTION_NAME_TAKEN, which says whether a deleted collection held it and offers a free name. (#471) - An assistant answer about how Loom works cites the documentation it used as
[help:<page> § <section>]; Loom checks each citation against the docs the server ships, and the dashboard links a verified one to a new Help page at that section. An answer that names an op or setting the server neither serves nor documents is marked unsupported, and the names are listed inunknown_names. (#498) - The dashboard's assistant chat is a panel the header opens on every page (docked beside the page on a wide window, over it on a narrow one, a sheet on a phone), and its conversations are kept in the browser, for the signed-in person only, for 30 days from last use. (#494)
- Each assistant question names the dashboard page it is asked from in a chip the person can remove. An "Ask about this" button on a memory, a document and a Recall lab result opens the assistant with that item named. (#518)
- The Library's Add document takes many files at once, picked or dropped: each becomes its own document, three are sent at a time, and a list says where each one is, with Retry failed and Stop. (#486)
- The Insights tab and the Assistant page's panels are now starters in the assistant panel: each reads Loom's own card (inventory, audit, mentor hints, a subject's memories, documents, tune drafts) into the conversation, with or without asking the assistant. A streamed answer shows each tool call as a step while it runs and lists what it read under the answer. (#524)
- The assistant panel offers Copy under every answer, Retry and an edit of the last question (also with the up arrow in an empty field), and "Ask again with sources" on an unsupported label. A streamed answer is shown done as soon as its text ends, with "Checking sources…" while its citations are checked. (#536)
- A process with
LOOM_CELLset serves only while its cell's rows in Postgres are read and accept its configuration; until then it answers 503AUTH_UNAVAILABLEto everything but its health probes and runs no worker. WithLOOM_CELLunset nothing changes. (#468) - A cell serves only the orgs it holds: a call for another cell's org answers 409
WRONG_CELLand writes nothing, and a token's own org new to the cell is placed through the router. WithLOOM_CELLunset nothing changes. (#473) - On a cell, a CLI command refuses, before it runs, an org the cell does not hold. Every command that connects declares the orgs it writes, or why it writes for none. A cell needs the Postgres authority and authentication on every transport (cells). (#484)
loom router serveruns the router's placement API and org lookup from its own Postgres directory (LOOM_ROUTER_PG_DSN,loom router migrate). A cell places a new org through it, and a client finds an org's cell withGET /cells/lookup. (#497)- On a cell (
LOOM_CELL), the first cell inrecordingrecords a new org only while the router's directory answers that the cell isattaching; while the directory is unreadable or answersserving, a new org answers 503 and the orgs the cell holds keep serving. The router answers that read atGET /cells/v1/cells/<cell>, for the first cell's own credential only. (#519) - The router answers a placed org's
GET /cells/lookupfrom its cache while its directory cannot be read, and the lookup before any cell is registered answers 503 with the codeNO_CELL, which clients match instead of the message. (#520) - The Python and TypeScript SDKs raise
WrongCell(aPreconditionFailed) for the server's 409WRONG_CELL, with.organd.cellnaming the org and the cell that holds it, so a client can look that cell's host up with the router'sGET /cells/lookupand call it. (#521) loom login --org <org>(andloom.login(org=…)) signs in at the org's own cell, as the router'sGET /cells/lookupnames it. With no router, or no cell registered yet, it signs in at the server it was given; it waits out the router's 429 for at most--waitseconds (default 120); on any other answer it refuses without starting a sign-in. (#522)loom router servenow routes every request that is not its own to one cell: a cell's own host to that cell, SCIM on the shared host to the first cell, a token-bearing request on the shared host to the cell that accepts its issuer, and everything else to the first cell (or, before one is registered, toLOOM_ROUTER_UPSTREAM_URL). Requests and answers are streamed through, with the client's host, scheme and address inX-Forwarded-*. (#523)loom cell limitcloses, reopens or bounds a cell to new orgs,loom cell credentialmakes a router credential (new) and rotates a cell's (--next,--finish,--revert), andloom cell which --orgnames an org's cell.limitandcredentialwrite the router's directory alone, through a new operator API (whichreads the public lookup and sends no credential) the router answers with its operator credential, so they work while a cell is down. (#526)- Every deployment answers
GET /v1/health/deploymentwith the deployment id it verified at boot, andLOOM_CELL_PROBE_PORTstarts a probe listener that also answers the values underloom cell prepare's nonce keys in Postgres, ClickHouse and the document bucket, with the bucket and prefix it stores under. (#529) - The router registers a cell through its operator API (
POST /cells/v1/operator/cells/<cell>/register, whichloom cell preparecalls), once the cell's probe listener answers its deployment id and the nonces prepare wrote to its stores; the first cell is probed at the router's ownLOOM_ROUTER_UPSTREAM_PROBE_URL. (#535) loom cell prepare --cell <cell> --host <host> --credential-sha256 <sha256>registers a cell in the router's directory once the router has read back, through the cell's probe listener, the nonces it wrote to the cell's Postgres, ClickHouse and bucket, and then writes the cell'sfirst, tenancy mode and own rows; it is safe to rerun. (#538)
Improvement
- A search that serves excerpts (
bodies="excerpt", theloom_searchdefault) now shows a decision or constraint row as stored (the write-time clip: 1,200 characters, more for an extracted answer) instead of its first 200 characters. (#276) loom decisions extractand the document-source sync read a design document whole and write each decision as a maintainer would, its outcome, reasons and rejected options in plain words, each claim cited by quotes of the document and checked against them before it is recorded. (#277)dev/loom_ingest.pyuploads a file's bytes and ingests them withknowledge.ingest, so the server's extractor parses every format and the CLI waits for the job and prints the revision it published; its--upsertflag is removed (every ingest publishes the source's next revision).loom demoingests its file the same way. The Python SDK gainsknowledge.upload,ingest,ingest_status,ingest_cancel,wait_for_ingestandingest_file. (#280)- The dashboard's Library and a repository's documents add a file by uploading it for the server's extractor and following its ingest job to the revision it publishes. Each file is badged beforehand with the format this deployment reads it as, or refused when no extractor reads it; the browser no longer parses PDFs or Word files. (#283)
- The dashboard's Library finds a passage inside its documents. Each hit is cited as an agent cites it: the document, the revision it was read from and where the passage sits, in lines, a key, pages or slides, or a sheet's rows. A hit opens its document. (#284)
- In a DOCX with a
Titleparagraph, the reference extractor puts the document's headings under the title, so section paths and heading levels follow the document's outline. (#295) - The reference extractor reads a PDF as it is laid out: its tables become typed tables and datasets with rows cited by page, headings come from the type when the file has no outline, two-column pages read in order, sideways pages read like any other, and running headers, footers and page numbers are left out of the text and listed. (#292)
- An XLSX header over two rows, a cell merged across columns grouping the names under it, names each column from both rows (
2025 / H1,2025 / H2); before, the second row of names was read as a row of data and the columns were2025and2025 (2). (#308) - A PDF page or PPTX slide whose text is read but which shows a picture OCR does not read (one under a fifth of it, a picture fill or background), a chart, a diagram or an embedded object is now named in the artifact (
attrs.images_unread) and in one warning, and a DOCX of text with pictures, charts, diagrams or embedded objects warns; before, such a file reported complete coverage and said nothing about the text inside them. (#301) - A
gdrivesource can sync the.docxfiles in its folders: namedocxin itsinclude(or tick .docx documents in the dashboard's form), and each.docxfile goes to the extractor like a PDF or a.pptxdeck, its original kept for download. The defaultincludestays the four kinds it was. (#314) - Document sections that are meeting notes, status reports or bookkeeping, and record no decision, are labelled noise at ingest. A noise hit ranks below every knowledge hit close to the query and stays out of
context.packandmemory.stack; a decision written in meeting notes is still served.LOOM_KNOWLEDGE_NOISE_FILTER=falseturns this off. (#293) code.index_statusreports adashboardreach kind: whether the dashboard calls an operation. (#315)- The assistant panel's inventory starter shows what the namespace holds and who appears in it; each user and agent there is a button that shows what it remembers, so no id has to be typed. (#306)
- The Claude Code and Codex plugins name the oldest Loom server they support, so a later release can warn a session started against an older server, in one line, and the
loom-statuscheck reports the same.GET /v1/healthreports the server'sversion. (#328) - An extractor's artifact whose line or character range ends past the uploaded original is refused, and the ingest job fails
invalid_artifact. Each ingestion fault now has a test showing the job fails with its error code while the earlier revision keeps serving. (#368) - Meeting notes written by a meeting tool from a recording are now labelled: their transcript, invite header, next steps and footer rank below close hits and stay out of a context pack, while their summary and topic sections stay content. A stated plan ("we'll enable the cache by default", "the plan is to ...") now counts as a decision. (#344)
- An HTML data table is extracted as one block, a row per line with its cells joined by a pipe, as a DOCX table is; before, each cell was its own paragraph, so a row's values were cited apart from each other. A table that lays out a page (a heading, list, code block, table or second paragraph in a cell) still reads as the blocks its cells hold, and a table of one column of text, unlike a DOCX one, is a paragraph without
attrs.table. (#302) - A code-index tier that keeps failing on a repository is now recorded with its error kind and the one fix, and code answers,
code.index_statusandcode.reposname it until the next pass that succeeds clears it. (#347) - Model-call spans carry the OpenTelemetry GenAI attributes as the conventions now name them, so HyperDX's LLM view and Langfuse read Loom's provider, operation, token split and cost as they are. Langfuse no longer counts a prompt-cache read twice in a generation's total, and an embedding call is a Langfuse embedding observation. (#392)
- A code-index tier that keeps failing is a
code_tier_failingproblem in/v1/health, the dashboard banner andloom doctor, naming the tier, its error kind, since when, how far behind and the fix; the repository card names the branch and shows the same. (#387) - Every span Loom sends to Langfuse carries the request's user, session, tags and trace name, so Langfuse 4's user and session views and its observation-level evaluations see whole traces. Search stages arrive as
retrieverobservations, which also puts a search on Langfuse's agent-graph view. (#390) - Failing code-index tiers are exported as the
loom.code.tier_failingandloom.code.tier_lag_commitsgauges, with an alert on the "Loom — Code Plane" dashboard and an optional PrometheusRule in the Helm chart (alerts.prometheusRule.enabled). (#398) - The assistant panel's Memories, Documents and Improvements starters, and the inventory's missing-count reasons, describe a namespace, its findings and its drafted changes in plain words; the exact op, setting and command names a fix needs come in a new
refsfield onmemory.inspectviews,knowledge.inspectfindings and decisions andnamespace.tuneproposals, which the panel lists under "Exact names". (#361) - Each release's
install.shinstalls that release, so a pinned or saved copy installs the same version every time, and a download cut short runs nothing. Every release rehearses the one-line installer in clean Debian, Ubuntu and Alpine containers on amd64 and arm64 before it publishes, then checks the published release from outside. (#345) - Every error with a
code, over HTTP and MCP, now carriescategory(input,auth,capacity,dependency,configurationorinternal) andretryable, and the SDK errors expose both. Over MCP, a refusal of a namespace the token may not read adds anextstep: check the name againstnamespace.list_minebefore requesting a grant. (#303) - Each release attaches
agent-setup.txt: the steps a coding agent follows to install that release on its own machine and prove it works, pinned to the release's version and commit, with content-level checks over real ops and a fixed PASS/FAIL report./v1/healthon a released image now reports the commit it was built from. (#411) - A fresh installer stack now searches by meaning with no key and no network: the server image carries a small local embedding model (bge-small, CPU), pinned to one revision and checked by sha256, and the stack's
.envselects it. An existing install keeps the embedder its.envnames. (#410) - A code-index tier that fails on a permission error naming a file now records the file and a
permissionkind whose fix namesLOOM_CODE_REPOS_SHARED_GID. Before, the record held onlyPermissionError, or a harness tail classed as an indexer failure. (#429) - When the TypeScript compiler tier cannot install a repository's dependencies, its tier-health record holds the installer's name, exit code and the end of its output. Before, it held only
RuntimeError. (#432) - An assistant answer's own token, or any token delegated from one, can no longer ask the assistant:
assistant.askandPOST /v1/assistant/chat/completionsrefuse it withASSISTANT_SELF_CALL(403). (#480) - When setup cannot write the analyst user, or a probe gets an answer it did not expect,
/v1/health'sanalysis_user_failingproblem names ClickHouse's error code and error name and, for a cause Loom knows, what it means and how to fix it. (#472) - Each assistant answer now comes back with
support(supported,unsupported,uncheckedornone), and the dashboard labels it in words. A greeting, a capability reply or an abstention carries no label, where it was marked unsupported before. (#476) - The assistant's refusals carry a
detail.reasona client can branch on (not set up, auth off, starting, failing, an org-wide, narrowed or short-lived token), and/v1/health/readyreports anassistantcheck where one is configured. (#485) - The
code_tier_failinghealth problem now gives each failing tier's reason and fix, and leaves out a lag of 0.loom doctoralso names each entry it lists (up to five, then it points tocode.repos) as repository@branch (org/namespace), with the error line that says why; a platform admin's/v1/health(the dashboard banner included) does so for each listed entry that is not restricted. (#470) - Dashboard errors say what happened and whether trying again can help, without guessing a cause. A refusal that is not an edit race no longer reads "Out of date — another writer changed this", a setup problem no longer says "try again in a moment", and a failed read says why it failed. (#474)
- A compiler-tier delta is applied a round of files at a time, so the memory its read costs is one round, bounded by
LOOM_CODE_SCIP_DELTA_ROUND_MAX_NODES(about 2.5 GiB resident in the worst case at the default, and the apply's rows for that round about as much again), plus a small record per entry that the node cap bounds, not the whole delta. A large tree's full run can raiseLOOM_CODE_SCIP_DELTA_FILE_MAX_MBandLOOM_CODE_SCIP_DELTA_MAX_NODESwithout raising the worker's memory. (#478) - A token limited to a list of namespaces now records where it saves: the token form's Saves memories to picks a memory namespace of the list the token may write, the server signs that choice into the token, and switching namespace on the dashboard no longer moves where the token saves. A list token can therefore search a shared namespace first and save to another. (#496)
- The assistant answers "how do I…" for the person asking: it knows the kind of credential they ask with and the role it has in the namespace, and answers how that role is raised from Loom's documentation. A question that does not plainly ask how to use Loom is searched in the namespace's memories and documents and in the docs together, so a fact the namespace holds is answered from the namespace. (#509)
- The Library shows where a collection sits: its path from the top, each ancestor a link (on a phone, one back button), and a Collections section listing the collections inside the one shown (or the top-level ones on My files and a namespace). (#487)
- An assistant answer whose sources were not all found in Loom now leads with how many were ("Partly confirmed: 2 of 3 sources found in Loom", "Couldn't confirm: 0 of 2 sources found in Loom"), and each source reads "Found in Loom" or "Not confirmed" with its reason. (#516)
- The dashboard no longer shows a collection as a namespace: the namespace switcher and Browse list namespaces only and point to the Library for collections, header search and the copy dialog list namespaces only, the token form, Flow and admin Users show collections in their own group by title and path, and an account that reads only shared collections opens on the Library. (#515)
- The Library lists the collections shared with you in their own "Shared with you" section, each saying where it sits. With no namespace at the top, it opens your own files instead of an empty page, and the new-collection dialog says that a top-level collection belongs to the org. (#532)
- The Claude Code and Codex plugins call the conversation-recording control by its dashboard name, the Record conversations switch on the dashboard's Conversations page, including in the session line they print when recording is off. (#528)
- The dashboard opens on Memories, and a new account sees a short Getting started checklist there: connect an agent (the token form opens in place), its first memory, and, for whoever can set them, the models. Each step ticks from what Loom holds: a token's first call, an agent's memory, the memory count. (#537)
- Every empty page in the dashboard now says what will appear there and offers the action that fills it: "Connect an agent" opens the token form in a dialog in place, and the text names no API operation or server setting. (#540)
loom install --client cursorwrites Cursor's MCP entry on thecodingtoolset, as the dashboard's Connect dialog and the token setup already do.--toolsetstill picks another listing. (#548)- A dashboard control your role cannot use is now shown with what it needs ("Needs writer on . An admin of it can grant that.") instead of being hidden, in the members dialog, the namespace settings, Structure, the Library, the memory drawer and the assistant's cards. (#544)
- Plugin marketplaces, the MCP Registry and the package indexes now list the project as "ClickHouse Loom" by ClickHouse; install names (
loom,clickhouse-loom,@clickhouse/loom,io.github.ClickHouse/loom) are unchanged. (#558) - The supported versions page states which releases get fixes (N and N−1), that an SDK at X.Y works with a server at X.Y or X.(Y−1), what the coding-agent plugins'
minServerVersionmeans, how a removal is announced, and how to check a release image's signature. (#412)
Performance Improvement
- The ingest worker takes the next job as soon as one ends instead of waiting 2 s after every job, so a queue of small documents drains at the speed of its jobs.
LOOM_KNOWLEDGE_INGEST_INTERVAL_SECONDSnow defaults to 0; a pass that finds no job still waitsLOOM_KNOWLEDGE_INGEST_IDLE_INTERVAL_SECONDS. (#401) - The code index stays linear in a file's size on files with many same-named definitions, calls, classes, template literals, imports, deep directories, Go
replacedirectives or tsconfigpathspatterns; such a file took minutes to hours to index or to show in the PR overlay. (#482)
Backward Incompatible Change
- A server whose
LOOM_BIND_HOSTis not a loopback address refuses to start withLOOM_MCP_AUTH_MODE=openor with aLOOM_JWT_SECRETbelow RFC 7518's minimum key size (an HMAC secret shorter than 32 bytes or an RSA key under 2048 bits; only the length is checked), or a gateway or inline end-user issuer key under 2048 bits, unlessLOOM_ALLOW_INSECURE=1;loom doctorand/v1/healthreport both conditions.scripts/serve.sh, run from a checkout, treats a non-loopbackLOOM_LISTEN_HOSTas the bind address when the environment does not setLOOM_BIND_HOST(the image sets both). The development compose file and the per-worktree ClickHouse start script publish ClickHouse on127.0.0.1only. (#327) - With
networkPolicy.enabled: false(the default) the Helm chart now renders an egress-only NetworkPolicy that keeps the Loom pods off the instance metadata service and allows every other egress. Pods that took cloud credentials from the metadata service, and releases whose own policies limit the pods' egress, setnetworkPolicy.guardMetadata: false. After ahelm upgrade --reuse-values, setnetworkPolicy.egressExceptagain to pick up the added IPv6 address. (#367) - Settings → Tokens has one form for every token: it reads everything you belong to (the default) or only the namespaces you tick, saves to the namespace you pick, and the result shows the setup for each MCP client or a script.
POST /v1/me/setup/mcp_configis removed:POST /v1/me/service_accounts.createmints every token, takesnamespaces: "*"(the default) or a list, and returns each MCP client's setup. The SDKs' token methods are renamed: in Pythonagent.account.service_accounts(),create_service_account(...),revoke_service_account(...)andmcp_config(...)becometokens(),create_token(namespaces="*", *, name=None, role=None, ttl_days=90, saves_to=None)andrevoke_token(name); in TypeScriptserviceAccounts,createServiceAccountandrevokeServiceAccountbecometokens,createTokenandrevokeToken, theServiceAccount*types becomeToken*, andTokenRowdropskindandclient. (#326) - Under either authority,
knowledge.update_metadatawithreindex=truenow re-indexes also when the metadata is already current, so each such call re-embeds the document, andknowledge.bulk_addrefuses a batch that names asource_uritwice (422VALIDATION_FAILED, nothing written): send one entry per source. (#405) - A server on a non-loopback
LOOM_BIND_HOSTwithLOOM_SELF_SIGNUP=opennow refuses to start unlessLOOM_IDENTITY_PROVIDER=trust-networkorLOOM_ALLOW_INSECURE=1is set or sign-in is OIDC (LOOM_OAUTH_LOGIN_PROVIDER=oidc, which closes sign-up), andloom doctorand/v1/healthlist open self-signup on a reachable bind. (#483) - A trusted caller (
LOOM_TRUSTED_CALLERS) can no longer act withX-Loom-On-Behalf-Offor a token's ownsa:subject (a dashboard, admin, agent sign-up orloom mint-tokenservice account). Of thesa:subjects, it now acts only for an active bot and its agents, so the direct grants of a token under ansa:subject stop being reachable once the token ends. A token minted under a subject withoutsa:is acted for like a person's until that subject is revoked outright. (#531) - The default MCP listing (
core) shows 13 tools instead of 34 (ten memory ops plusloom_search,loom_activateandloom_op), and the listed tools say when to use each one rather than a neighbour. Oncore, documents, working memory, profile, namespace and erasure ops run throughloom_op; HTTP and the SDKs call them as before. A client on the default listing with no transcript hook no longer records conversation turns; it mounts?toolset=allto keep them. Seven experimental ops that were listed onallonly becausecorecarried them leavealltoo (andmemory.related, the one read among them, leavesreadonly):conversation.create,memory.related,knowledge.delete,profile.set,profile.append,working_memory.replaceandworking_memory.delete. (#546) - A document source can be registered on any shared namespace or collection, not only a repository's trunk code namespace, the Library shows the sources of the place in view, and a source is retired when its namespace is deleted or erased. The source ops no longer answer
NOT_A_CODE_NAMESPACE;knowledge.source.add,rotateandsync_nowanswerNAMESPACE_INACTIVEfor an inactive namespace andPERSONAL_NAMESPACEfor a person's own collection or namespace.knowledge.source.addanswers 409ERASURE_RUNNINGwhile an erasure of the namespace runs, andnamespace.restorebrings a deleted namespace back without its sources: remove each withdelete_documents=trueand add it again. (#541) - An agent's claim code works once: a person enters it at
/oauth/agent-claimwith a password of their own, which replaces it, and no sign-in (dashboard, OAuth authorize,loom login) takes the code. This holds for agent accounts made before this release too: one already claimed by signing in with its code is claimed again at that page with the same code. The claim ends every token minted under the account before it, so mint those again; whoever holds the code claims first, so claim at once. (#336) collection.createon a taken name answers 409COLLECTION_NAME_TAKENinstead ofPRECONDITION_FAILED; the SDKs still raisePreconditionFailed, and a client that matches the code matches the new one. (#471)namespace.restoretakes each field only as its column holds it: a snapshot payload with a value its column cannot hold is refused with 422VALIDATION_FAILEDbefore anything is written, where most such payloads failed with a 500. (#158)- On any deployment, a
loom codecommand that reads its repository entry and whose org (--org, elseLOOM_CODE_ORG) has no configured entry now refuses instead of indexing the first entry's checkout under that org. (#484)
Bug Fix
- Most read-only knowledge ops (
knowledge.get,list,ingest_status,formats,source.list, ...) draw from their ownknowledge.readrate bucket (50 burst, 10 per second), so a page of reads or an agent polling an ingest no longer spends the budgetknowledge.addneeds, and is no longer refused after ten calls. (#291) - The Claude Code and Codex plugins hand the file rules recalled before an edit, the issue titles the code shunt quotes and the saved-brief list to the agent inside recalled-data blocks whose closing tag carries an id only the install can compute, so stored text cannot end them, and the conversation flusher and the code-shunt hook no longer follow or read a symlink, hard link or another user's file planted at their temp files. The code shunt's decision log moves to
$TMPDIR/loom-shunt-log-<hash of the uid>.log, and a shunt denial whose budget cannot be recorded now lets the call through. (#321) - A JSON-RPC batch to
POST /mcpholds 1 to 64 requests; a larger or empty batch is refused whole with Invalid Request before any entry runs. (#330) - The reference extractor reads a PDF font's ToUnicode map in time linear in its size, so a PDF whose map opens blocks it never closes no longer holds the extractor until its timeout. (#339)
- The memory inspector's Last recalled now counts every agent read that recalled the memory (searches, context packs and the memory stack,
memory.ask, procedure matches, the pre-edit file read), so a memory an agent was shown no longer reads "Never recalled". The ranking credit is shown beside it as Recall credit, andmemory.getwithinclude_recall_breakdownanswersserved_countandlast_served_at. (#311) - A dashboard link carrying
#token=signs the browser in without asking only when it answers the SSO sign-in this tab started, or when the token is for the account already signed in; any other link asks first, naming the account it signs in as. (#320) - A
gdrivesource no longer stores, chunks and embeds the base64 bytes of the pictures pasted into a document: a line of pictures becomes an[image-only: …]marker, as on a page store's page, and documents synced before this release are converted again once. (#312) code.search(and the code grounding ofmemory.ask),tracker.askand the decision extractor read identifiers in linear time, so a query holding a long run of capitals or digits no longer blocks them for seconds or minutes.memory.ask's own identifier patterns are fixed in #317. (#342)- Indexing a FlatBuffers schema with a long run of blanks inside a union member or a block header no longer stalls the code-index worker. (#357)
- The Claude Code and Codex plugins' prompt-pack and code-shunt hooks no longer stall a prompt submit or a Bash call for seconds on a prompt with a long whitespace run after an opening tag, or a sed or awk program with a run of backslashes. (#365)
- The C/C++ compiler tier fetches submodule pins with the repository entry's GitHub token, so a repository with a private submodule of its own owner no longer stalls its compiler tier at the first pin the checkout lacks. (#341)
- A knowledge source's sync no longer overwrites a document a person writes at the same address while the sync is fetching and converting that file. (#353)
- An entity name longer than 256 characters once normalised (NFKC turns a
filigature intofi) is treated like one longer than 256 as written: it is no entity, and a point write's fact subject or object of that kind is refused with a message that says so. (#366) - Erasing a user no longer un-cancels another writer's document ingest that the user had cancelled: the cancel is kept without the user's identity. (#362)
- A synced knowledge document full of unclosed image or link markup no longer holds the server for seconds while decisions are extracted from it. (#370)
- Under Codex, the loom-session and loom-status skills use the installed loom plugin's own copy even when another marketplace holds an older copy of loom, and
loom doctor --clientwarns when only another marketplace's copy is installed. (#372) - The reference extractor guesses a CSV's delimiter in bounded time: a CSV whose first lines are full of quotes and delimiters is sniffed in well under a second instead of seconds before any row is read. (#343)
- An audited operation that declares no audit subject now records its text inputs as a SHA-256 and length, so
loom.audit_logno longer keeps a decision's reasoning, a moved memory's value, an imported document or a restored snapshot after they are erased. Rows written before this release keep their text. Text fields in audit subjects written from this release, on the default path (amemory.deletereason, adecision.recordscenario, ...) and in a cross-promotion's or copy'srationale, a proposal'sreasonand a supersession'sclosed, are{sha256, chars}objects where they were strings, so a reader ofaudit.listorloom.audit_loghandles both shapes; a supersession row's actor is the writing principal, and a policy decision's row carriespolicyin place ofreason. (#334) - The Claude Code and Codex plugins now work on Windows (x86_64 and arm64, with or without Git Bash) and on macOS checkouts reached through a symlink; a CI workflow runs the plugin suite and a real Claude Code and Codex session on Linux, Alpine, macOS and Windows. (#313)
- Erasure deletes a user's or a namespace's traces on Langfuse 4 as well as Langfuse 3. On Langfuse 4 it finds them through the observations list, because Langfuse 4 no longer serves the trace list. (#348)
memory.rejectrefuses a locked memory with 423LOCKEDand writes nothing, asmemory.deletedoes; a negative-outcome quarantine frommemory.outcomeleaves a locked memory as it is (its negatives keep counting). (#354)- A repository's tracker links are read per org: a link set with
code.repo.set_sourceapplies only to the repository in the org that set it, never to another org's code namespace of the same name.LOOM_TRACKER_CODE_NAMESPACE_BINDINGS_JSONlinks only the operator's own repositories (LOOM_CODE_REPOS_JSONorLOOM_CODE_REPO); a self-service repository is linked by its maintainer. A branch added withcode.repo.add_branchfrom an operator repository reads that repository's links. (#331) - A
commit:orerror:name is one graph node everywhere: entities, relations, code facets, co-occurrence edges, query triples and the seeds ofmemory.search,facts.traverseandfacts.pathspell it the same way, so a search forerror:Timeoutsreaches what a write stored for it.facts.listandfacts.relatedmatch names as the writers store them, andfacts.relatedreturns the name canonicalised like a write. (#383) prompts.evaluatefinds a prompt version's traces and scores on Langfuse 3 and Langfuse 4. Before, it found no traces on either and always answeredshould_promote: false. A failed Langfuse lookup or score read now answers 502BACKEND_UNAVAILABLEinstead of a "no traces" or "no scores" verdict, and traces sent before this release carry no prompt tag, so an evaluation counts only traces sent after the upgrade. (#389)- A document download or upload, an inbox watch, a claim release, a token mint and the MCP primer's profile read that are admitted to a restricted namespace now write the
namespace.restricted_accessaudit row, as the op gate does for the namespace a request names. (#358) - A user erasure now deletes the cached
memory.askanswers and the precedents that quoted a consolidated summary of the user's memories stored under another identity, with or withoutpurge. Apurge=trueuser erasure now also removes what the user's memories fed through their keys; before, it removed the memory rows first and then found none of it. (#360) - The C/C++ compiler tier re-runs the CMake configure when a merge adds a translation unit or edits the build, and before every full index, so a
.cppfile added after an entry's first index gets compiler-grade symbols instead of staying on the Tree-sitter tier. (#379) - A
read_only-locked memory keeps its live expiry, or none, through amemory.setthat restates it: its memory type's retention no longer stamps a new expiry on a locked row, because the lock refuses an expiry change. (#381) - A cached
code.askanswer that quoted memories of the bound memory namespace (LOOM_CODE_ASK_MEMORY_NAMESPACE) now records that namespace and the quoted keys, and an erasure of the namespace or of a user whose keys it quoted deletes it, asmemory.redactdoes for a quoted key. The residue census counts these answers, so an erasure that leaves one fails (except under a user purge, where the census cannot read the user's keys).code.askreads only the bound namespace, whatever the defaultmemory.searchscope. (#332) - A
memory.applyrow whose write fails unexpectedly reports the kind of error and the request id instead of the error's text, and a typed Loom error keeps its code and message. The history policy gate's errors in a 403,blacklist.add'ssweep_error,blacklist.sweep'serror, a failedloom_searchsection'serror, an extraction batch'slast_error, an erasure'sfailure_reason,code.index_statusandops.healthworker errors,ops.healthprobe details and/v1/healtherrors name the error kind, not the store's text, including rows written before the upgrade. (#329) - The MCP session-start instructions now put the caller's profile after Loom's own rules, so on the
codingtoolset a client that cuts long instructions (Claude Code at 2,048 characters) keeps every rule however long the profile is. (#409) - The Claude Code plugin's
X-Loom-Code-Scopeheader names the checkout the session works in. Before, it named no checkout for a plugin installed from a git marketplace, and the plugin's own checkout for one installed from a local clone. (#415) - A tracker source whose
github_prshas notest_typesorsrc_componentscolumn now meets the record contract, with both served as[]and reported as defaulted, instead of pausing every tracker read for its namespaces. (#416) - When the expiry sweep writes a memory's post-action but fails to update its search vectors, it records the miss as a projection failure, and the projection repair applies it later. Before this change, a demoted memory could stay hidden from search, and an anonymized one could keep its entity names in the vector tables. (#414)
/v1/health?include=mirrorsand the/v1/health/readymirrors check count the host's git mirrors instead of listing each entry'sorg/namespacelabel. A model call the inference gateway refuses or defers, by an answerLOOM_INFERENCE_IDENTITY_REFUSALS_JSONlists, now has a message with only the gateway's status and code. (#420)- A new setting,
code_repos_shared_gid, names the group that shares the git mirrors and compiler-tier checkouts undervar/code_reposbetween the host operator and the containers (uid 999): what Loom creates there is then writable by that group and closed to other users, so the host'sloom codejobs and the containers can fetch into and check out what the other created. Unset (the default), mirrors and checkouts are 0700. (#417) - A first write into an unregistered namespace that already holds data or a live grant now registers it with no owner, so the writer cannot complete its registration; an admin does. Self-signup and agent sign-up now also refuse a name that a task inbox names as its owner or as a poster, that a poster rule names, or that posted a task.
task.list posted_by_melists only the tasks whose poster subject is the caller's, never tasks that merely carry its name as their agent label. (#421) - The code index reads a checkout's
package.jsonandyarn.lock, and each compiler tier's output delta, only as regular files under a size cap, never through a symlink; a delta is capped atLOOM_CODE_SCIP_DELTA_FILE_MAX_MB(180 MiB) andLOOM_CODE_SCIP_DELTA_MAX_NODES(6,000,000{/[/,bytes), both checked before it is parsed. A refused file fails that pass, as an unreadable one does. The ingest checks a mirror file's size before reading it, so a file overLOOM_CODE_INGEST_MAX_FILE_BYTESis recorded unread, and the TypeScript resolver's manifest and barrel parsing takes time linear in the file. (#322) code.repo.set_access, the code namespace registrar and the boot-time tracker namespace registrar now hold the namespace's account claim while they read and rewrite its directory row, asnamespace.registerandnamespace.deletealready do. A namespace change that lands at the same moment as one of them can no longer be undone by a stale copy of the row. (#425)loom reembedreads each page's rows again just before it writes their new vectors, and skips any row deleted, tombstoned, or given other text or another agent or user after the page was read, instead of writing back the text it read. When such an old-model row is still stored,--delete-oldleaves that org's old-model rows for a later run. (#426)- The
code_graph_statsworker pairs test files with source files in a thread instead of on the event loop. On a large repository the pairing held the loop for longer than the supervisor lease allows, so the deployment stopped every worker about every 20 seconds and no background pass finished. (#427) - The image and
scripts/install-code-toolchains.shinstall pnpm 12.15.0 instead of 12.3.1. The TypeScript compiler tier can now install the dependencies of a pnpm workspace whosepnpm-workspace.yamlsets lockfile options, which pnpm 12.3.1 refused to load. (#431) - A compiler tier no longer records a failure when only the
git gcit runs on an entry's checkout fails; the failure is logged. Before, such a record stayed failing after its cause was fixed, until the tier's next run that was due, up to a week for the full C++ run. (#434) - A tree-sitter parse of a repository file stops reading it after
LOOM_CODE_PARSE_TIMEOUT_S(3 s of CPU time by default); the file then takes the reader's parse-failure path, which for a file of a known language is chunks without symbols with the parse error recorded (the shell detector reads it as not shell). The PR overlay thatcode.callers,code.impactand review briefs read parses a PR's files in a thread, one at a time per namespace and once per file per namespace for concurrent requests, starts no parse afterLOOM_CODE_PR_OVERLAY_PARSE_WINDOW_S(10 s), and counts the files it did not fully read inoverlay_note. (#422) - Reading a parsed file's text no longer hangs once the reading thread has used more CPU time than the parse deadline allows. Such a read (the Python SQL-island extractor during a re-extract, for one) spun forever and stopped every background worker on its event loop. (#437)
- A user erasure now also removes what a consolidated summary of the user's memories fed: the summary's version history (read by
memory.history), its verbatim chunks and entity vectors, the graph rows extracted from it, and the org-wide copies promoted from it, with or withoutpurge. A user purge now also removes the search rows of an org-wide copy promoted from the user's own memory, which it used to leave. (#423) insights.querynow works on ClickHouse Cloud, where it stayed unavailable withSetting log_queries should not be changed. The analyst's scope key and password stay out ofsystem.query_logthere too, including the error text of a read a row policy refuses. (#428)insights.querynow works on a ClickHouse that takes custom settings under a prefix other thanSQL_, ClickHouse Cloud'scustom_among them. Loom finds the server's prefix when it loads the analysis secrets. (#442)- An operator entry in
LOOM_CODE_REPOS_JSONwhose namespace was added from the Repositories page is now skipped, with a warning in the log, instead of taking over that member's access settings and admin grants. A namespace a member created withnamespace.registeris still taken over when the operator lists it; review its grants first. An access row set on an operator entry is no longer listed once the entry leaves the configuration, instead of being listed as a page-added branch indexed with the operator's token. Both hold for registry rows this release writes; a row an earlier build writes, before or during the rollout, records no kind, and the #424 operator step covers it. (#424) - Every tree-sitter parse of a repository file or of a PR's posted files first runs in a child process held to
LOOM_CODE_PARSE_MEMORY_MB(256 MB of address space by default) andLOOM_CODE_PARSE_TIMEOUT_Sof CPU time; a file past either limit is indexed as chunks without symbols, with the parse error recorded, and the server process never makes that parse. (#435) - In an
append_onlynamespace the conflict check of amemory.bulk_setof two or more entries now gives every entry its own row, as it does a single write; it could merge an entry away or mark it duplicate or conflicted. (Entries with identical text are still merged by the bulk write's identity dedup, a check of its own.) NewLOOM_MEMORY_BULK_PEER_READ=per_factlets a bulk write read each fact's conflict peers withmemory.set's codes-first read, so a bulk write into a namespace of hundreds of thousands of memories does not run ClickHouse out of memory; the default,batched, keeps today's read. (#451) - An op whose ClickHouse statement runs out of the server's or the ClickHouse user's memory now answers 503
OVERLOADEDwithretry_after_s5, asinsights.queryalready did, instead of 500INTERNAL_ERROR. A statement that hits its own memory cap answers as before. (#458) - The code index reads Markdown, Python layout manifests (
setup.py,pyproject.toml,conftest.pysets), Dockerfile, Jenkinsfile, Angular, Vue, Svelte, Astro and dbt files, CMake command rows, and route and environment-variable facets, in time linear in the file's size. A crafted file, in an indexed repository or in the files a request posts for the PR overlay, could hold a serving replica or the indexer for minutes to hours. (#448) - The code index reads CMake files, Python manifests and entry points, Markdown references, the import candidate lists of Python, TypeScript, Rust, Go and Protobuf files, and the HTML comments of PR descriptions in time linear in their size. A crafted file in an indexed repository, a file posted for the PR overlay, or a PR description could hold a replica or the indexer for seconds to hours, or exhaust its memory. (#463)
- Memory search's key-match arm works in a namespace that holds a memory key with whitespace in it (
communication style). Before, every key-match read in that namespace failed and the arm added nothing. (#475) - A tracker source whose
github_prslacksaffected_file_pathsordiffno longer fails every read of a PR's files (pr_files,prs_touching_paths,pr_changeand the bisect scans) withUnknown expression or function identifier. The contract check reports the columns as defaulted, and the reads answer as for a PR that touched no file. (#477) - The token form's Only these namespaces starts with nothing ticked, lists every namespace you belong to (your personal one included), and shows the ticked ones as one search order that you rearrange with its Move up and Move down buttons, or on a desktop by dragging the row's grip (a touch screen, where a long-press drag is not what a person expects, shows the arrows only). (#481)
- The Loom assistant is told to write nothing before or between its tool calls, so on the dashboard's Assistant page a lead-in such as "Let me search the memories." no longer comes before the answer and runs into it, as long as the model follows its instructions. (#479)
- A deployment-wide lease whose renewal call hangs (a ClickHouse read past its deadline) is now cut short and retried inside the window before the lease counts as lost, so one hung call no longer ends a long compiler-tier full run. (#489)
/v1/healthreportsworkers_lease_absentonly when no process holds a live workers lease, so a web container that runs no workers no longer shows it while a workers container runningautoruns them. (#490)- On a single-user install, Settings → Tokens → Create token mints a token instead of failing with "cannot delegate access on local", and the token belongs to the install's org (
dev) rather thandefault. (#501) - The documented install command now saves the installer before running it (
curl -fsSL …/install.sh -o loom-install.sh && sh loom-install.sh && rm loom-install.sh), so a failed download stops with curl's error and a non-zero exit instead of exiting 0 with nothing installed. (#500) - The manual Python compiler-tier pass,
scripts/scip_harness/run_python_delta.sh, runs again: it failed on every entry withRegistryUnreadbecause it listed the configured entries before reading the self-service repository registry. (#530) - When a dashboard write is saved but lists and search have not caught up yet, the page now shows it as made: a pin stays pinned, an edit closes, a dialog closes, and a deleted document keeps its Undo. Before, the page showed it as failed and invited sending it again. A rename that the server answers this way before it has moved the old key says a second Rename finishes it. (#534)
Critical Bug Fix
code.searchwith a one-entrynamespaceslist authorizes that namespace before searching it, as it does each entry of a longer list; a caller who cannot read it gets 403.code.repo.set_sourcestores a link only to a tracker the caller administers, unless the repository's own links already hold it; a reader of the tracker gets 403, anyone elseNOT_A_TRACKER_NAMESPACE. Keeping, reordering or dropping a link the repository's stored links already hold is unchanged; a tracker only the deployment's map links counts as new.inbox.registernames the namespace that owns an inbox only to a caller who can read it. (#310)- Off the inference gateway, a namespace's
classify_endpointmust be https with no user name or password, andmemory.set(classify=true)posts only to the public address its host resolves to, with no redirect and no gateway identity headers; a failed call is reported indegradedby its kind alone.LOOM_MEMORY_CLASSIFY_PRIVATE_HOSTSnames the hosts an operator allows on a private address, over http too. (#318) - Blacklist regexes run on RE2 on every write, as they already did in the sweep: a pattern RE2 cannot run (a backreference, lookaround,
\Z), one that compiles to more than 300 RE2 instructions, or one that would take its scope's regexes past 3,000 together is refused atblacklist.add; the match runs on the blacklist's own threads, off the event loop, and a value too long to check against the regexes that apply (bytes times instructions past 250 million) is refused asBLACKLISTEDwith an emptypattern_id. Sensitivity detection of emails and JWTs runs in linear time on any value, and a write reads a value past 16 KiB in overlapping windows with a turn for the event loop between them. (#307) - A branch added from the Repositories page no longer makes its admins admins of the operator's configured entries for the same repository, and a revoke there no longer reaches them. The operator's admins still become admins of every branch added from the page. Copies already on an operator namespace stay until revoked: the code index documentation on adding repositories says how to find them. (#338)
- Self-signup and agent sign-up refuse a name that is a
LOOM_TRUSTED_CALLERSsubject, aLOOM_PLATFORM_ADMINShandle, or a subject holding a grant (a pending join request included), a namespace or an unrevoked token in the org, and sign-up tokens carrywriteronly. A trusted caller'sX-Loom-On-Behalf-Ofnaming a subject revoked or deprovisioned under any spelling it holds is refused with 403. (#305) namespace.adoptrefuses a restricted namespace and answers an unregistered name the same way,memory.sweep_expiredrequires a namespace,audit.listand the org-widelearnings.qualityrun the shared scope check (collections, restricted namespaces and delegated tokens included), and a tracker source is managed by an admin of its namespaces, not by whoever registered it. A private repository's code index stays unindexed and its mirror private until it is restricted, and a repository already indexed under another spelling is refused as a duplicate without naming the namespace that holds it. (#323)- The regexes that read request text on the event loop (dashboard asset names,
memory.setfile detection and its stack-trace hint,code.verify_citations,code.search,code.trace,tracker.similarandtracker.duplicate_checkframes,memory.ask,memory.set_from_messages,knowledge.search,loom_search, SCIM group filters and others) run in time linear in their input.tracker.similarandtracker.duplicate_checkrefuse a frame, symbol or path over 4,000 characters, andGET /assets/treats a file name over 255 characters as not a hashed asset and answers 404 for a path over 1,024 characters. (#317) - Self-signup, agent sign-up and every op that registers a namespace as new and makes its caller admin (
namespace.register,namespace.restoreinto a fresh target,collection.create, self-service code repositories and tracker sources) refuse a name that already holds data or a live grant with no registry row. The ops still accept it from a caller that may already act on it as admin, and with auth off. Before, any of these could take such a namespace and make the caller its admin. (#340) - A restricted repository branch added from the page now takes admin grants only from the operator's entries of its repository. Before, any org member who added another branch of the same repository from the page was copied in as its admin. After upgrading, run
code.refreshon each restricted branch added from the page, so its stray copies go at once, then review its grants: a member who held a copy there may have granted or approved others, themselves included. (#406)
Build/Testing/Packaging Improvement
- The document extractor image ships pypdf 6.21.4, which fixes security bugs in reading malformed PDFs. (#275)
- A document-parsing bench (
python -m bench.docparse) scores the reference extractor on a labelled corpus of design docs, specs, runbooks and decks as PDF, DOCX and PPTX: text, reading order, headings, tables, page locators and silent loss. (#289) - The server image's Go toolchain (1.27.3) and Node runtime (22.23.4) carry no critical CVE that has a fixed release, and a weekly image scan reports one before a release meets it. (#319)
- Knowledge ops have declared service levels (p95 and p99 for search, structured queries and ingest, and the time a small document takes to ingest), and
bench/knowledge_load.pyruns a mixed knowledge load against a server and checks it against them. (#394) - Both release images,
ghcr.io/clickhouse/loomandghcr.io/clickhouse/loom-extractor, carry a keyless cosign signature from the release workflow, socosign verifywith the release workflow's identity proves an image came from that release's tag. (#407) bench/scale_load.pyloads synthetic memories into ClickHouse and the Postgres authority and measures the memory ops, refusals, parts and merges, the vector leg, erasure, Postgres sizes and ClickHouse CPU per call;docs/benchmarks/results.md§ "Memory at scale" reports where memory's ceilings are and what a million memories cost at list prices. (#454)- The server is built with dnspython 2.9.2. (#491)
- The PyPI, npm and MCP Registry listings name Loom's shared long-term memory, document search and code index, with keywords such as
agent-memory,document-searchandcode-search; the PyPI and npm pages link the SDK reference pages. (#506)
Operator steps
- #291: a
knowledge.addoverride no longer applies to theknowledge.readops, at either tier. This holds whether it was set throughadmin/ratelimit.setor bydev/onboard_tenant.py, and whether it sits above or below the default. Those reads take theknowledge.readlimits: 50 / 10 per namespace and 150 / 30 per org, unless an override exists onknowledge.read. To keep a tenant's reads at the limit itsknowledge.addoverride gave them, set the same override onknowledge.read(POST /v1/admin/ratelimit.setwithop_class: knowledge.read, namespace or org). Runningdev/onboard_tenant.pyagain sets the namespace one, as it now raisesknowledge.read. A tenant with no override needs nothing. - #314: none to upgrade. To take the
.docxfiles of a folder an existing source already syncs, add a second source on the same folders withinclude: ["docx"]: it writes only the.docxfiles, whose addresses no source holds, and leaves the first source's documents alone (a sync never writes or retires a document another source holds). A deployment with no extractor fordocxcounts each such fileunconverted(no_extractor), as for PDF. - #321: anything that reads
$TMPDIR/loom-shunt.logreads$TMPDIR/loom-shunt-log-<hash of the uid>.loginstead. - #293:
- The label is written with the chunks. A document stored before this build, written through an N-1 replica during the rollout, or retitled by
knowledge.update_metadatawithoutreindex=true, keeps its chunks' earlier label (none reads as content) until they are written again. Run the relabel below once the last N-1 replica is gone. - Text documents. A text document, a synced one included, is relabelled by
loom rechunk --org O --namespace N --apply; a sync rewrites only the files that changed. That command also rewrites every ingested file in the namespace into a text document, so in a namespace that holds ingested files, run it per text document with--source-uri. - Ingested files. An ingested file is relabelled by a new
knowledge.ingestof its original.
- The label is written with the chunks. A document stored before this build, written through an N-1 replica during the rollout, or retitled by
- #327: a public deployment running
LOOM_MCP_AUTH_MODE=openswitches to token auth, and one with a weak root key rotates to an HMAC secret of at least 32 bytes or an RSA key of at least 2048 bits, and replaces anyLOOM_JWT_PUBLIC_KEYS_JSONor inline end-user issuer key under 2048 bits (or setsLOOM_ALLOW_INSECURE=1, only on a port others cannot reach). Someone who reached the dev ClickHouse from another machine now goes through an SSH tunnel. - #310: review existing repository links to trackers (
code.repo.set_sourcestate) made by maintainers who are not the tracker's admin (theqa_bindingkey inloom.code_index_state); links stored before stay in force until changed. During a rolling upgrade the review also coversqa_bindingrows written until the last N-1 replica stops, since N-1'sset_sourcehas no admin check. - #318: before upgrading, a namespace whose
classify_endpointis http moves it to https: from its first request the new build no longer calls a stored http endpoint (writes go unclassified), unless its host is listed inLOOM_MEMORY_CLASSIFY_PRIVATE_HOSTS. Also before upgrading, make each entry ofLOOM_MEMORY_WEBHOOK_PRIVATE_HOSTSandLOOM_INFERENCE_GATEWAY_HOSTSa bare hostname or IP literal: an entry with a port, brackets, a scheme, a path, a wildcard or a zone now stops the server at start. A deployment that runs its classifier beside Loom on a private address lists that host inLOOM_MEMORY_CLASSIFY_PRIVATE_HOSTS. A webhook receiver or tracker host that only/etc/hostsor a search domain names is either listed in its private-host setting or given its full DNS name; a git remote or submodule the egress proxy fetches is given its full DNS name. - #307: find stored regexes this build drops (
SELECT pattern_id, pattern FROM loom.blacklist FINAL WHERE pattern_type = 'regex' AND tombstoned_at IS NULL, thenloomlogsblacklist: dropping regex patternat cache load) and replace each with an RE2 form. - #312: none;
loom knowledge sync --fullconverts the stored documents at once instead of at the next full listing. - #344: chunks written before this change keep their label until they are relabelled as the #293 step says.
- #347: a deployment that runs a tier harness from cron under
loom code entry-lockadds--tier <tier>to that call to have its failures recorded. - #348: on a deployment upgraded to Langfuse 4 from a Langfuse 3 release earlier than 3.227.0, run Langfuse's historic backfill before relying on erasure; the Langfuse 4 lookup finds only traces in the observations list. An in-place upgrade from 3.227.0 to 4.76.0 was tested and needs no step. The erasures to request again are in the erasures item below.
- #158: a new deployment that wants the Postgres memory authority starts on an empty ClickHouse and follows Postgres; an existing deployment leaves
LOOM_STORAGE_AUTHORITYunset (it refuses to start withpostgresonce its ClickHouse holds memory) and needs nothing (its migration applies at boot). - #331: a self-service repository that relied on a glob in
LOOM_TRACKER_CODE_NAMESPACE_BINDINGS_JSONneeds a link from its maintainer (code.repo.set_sourceor the repository card's Trackers section). A branch of a self-service repository gets that repository's maintainer link when it is added; a link set on the repository later is set on the branch by an admin of the tracker withcode.repo.set_source. Every self-service branch of an operator repository added before this change has noqa_branch_ofrow and loses its glob link at upgrade. To find them: the registry entries (code.repos, sourceregistry) in the same org and repository as an operator entry. To restore one, an admin of the tracker links it withcode.repo.set_source, or the operator lists it inLOOM_CODE_REPOS_JSON; a repository maintainer who is not the tracker's admin is refused, as for any new link. - #367: a release whose pods take credentials from the metadata service, or whose own policies limit the pods' egress, sets
networkPolicy.guardMetadata: false(the first can also move to IRSA or EKS Pod Identity). A release upgraded with--reuse-valueskeeps its oldegressExcept, withoutfd20:ce::254/128, until it is set again. - #392: none for the shipped dashboards. Saved searches that filter on the old keys need the new ones; rows written before the change keep the old keys. In Langfuse, embedding calls are now
EMBEDDINGobservations, so a filter or saved view on typeGENERATIONno longer includes them or their cost. - #338: this stops new mirrors from page-added branches and does not remove rows written before it. Build N-1 replicas keep mirroring both ways during a rolling deploy, so run the review below after every replica runs this build. To review existing ones: list the grants on operator code namespaces whose
granted_byisrepo-maintainers:<namespace>with a page-added source namespace, and the subjects who hold them. Then list what those subjects did on operator namespaces while they held admin: grants whosegranted_byis one of them (readers they approved and admins they made; an admin they made keeps being copied across operator entries until revoked), rows ofloom.code_reposwhosecreated_byis one of them: a page-added branch they added, or an access row for an operator namespace written bycode.repo.set_access(a widened classification or join policy, which wins over the configured one; undo it by setting access again with the operator's values), and trackers they linked. Revoke or undo what should not stand. Also list thegrants.revokeand lowering audit rows on page-added namespaces whose subject was then an admin of an operator entry of the same repository: the fan-out of such a revoke took the subject off the operator entries too, with no audit row there, so restore the operator grants it removed. - #390: a deployment that sets
LOOM_CLICKSTACK_USER_ID_KEYSexplicitly keeps its own list, and addsloom.user_idandlangfuse.user.idto it. - #379: none. An entry's next full index picks up every translation unit added since its compile database was written. To get there sooner, run
loom code scip-fullfor the entry. A CI job that uploads C/C++ deltas and copies in acompile_commands.jsonwithout itsCMakeCache.txt, or with one made in another directory or naming a compiler the runner lacks, now configures from scratch, and every upload run invokes CMake before a full index and on a window that adds a translation unit or edits the build, so its runner needs CMake and Ninja, and clang for a configure from scratch. - #398: none; to page on a failing tier, enable the chart's rule or the dashboard's alert.
- #332: a deployment that has set
LOOM_CODE_ASK_MEMORY_NAMESPACE, now or at any time before, clears the answer cache once the newer build serves alone, with no provider rebuild running:TRUNCATE TABLE <LOOM_CH_DATABASE>.code_answers(loom.code_answersby default; upgrade procedure, step 9). A provider rebuild publishes its copy of the table byRENAME, so one running across the truncate would bring the rows back. Each cleared answer costs one re-synthesis. - #401: none. A deployment that relied on the pause sets
LOOM_KNOWLEDGE_INGEST_INTERVAL_SECONDS=2. - #305: review existing accounts whose name equals a trusted-caller subject or a platform-admin handle and that the intended person did not create.
loom claim-releaseremoves the account but not its tokens: revoke the subject's tokens issued up to now (loom revoke-token --subject <name> --issued-before <now>), then re-mint the real trusted caller's token, or move the trusted caller to a new subject. If a squatter already holds a platform-admin handle, remove it fromLOOM_PLATFORM_ADMINSfirst (a released name stays reserved, so the person cannot take that handle back), then list a new handle once the real account exists. Sign-up now refuses a listed handle, so a new platform admin signs up first and is listed after (deployment). - #397:
- Where Loom's ClickHouse user has no access management, an admin runs the SQL
loom ch-setup --print-analyst-sqlprints. Until theninsights.queryanswersANALYSIS_UNAVAILABLEand doctor says so. - A self-managed ClickHouse may add
access_control.xmlto close the dropped-policy window; without it, the window lasts until the next probe.
- Where Loom's ClickHouse user has no access management, an admin runs the SQL
- #406: after upgrading, once the last older replica has stopped, call
code.refreshon each restricted branch added from the page, so its stray admin copies go at once, then review its grants and approvals: a member who held a copy there may have granted or approved others, themselves included. - #418: none unless enabling the assistant. To enable it with Compose, add
LIBRECHAT_CREDS_KEY,LIBRECHAT_CREDS_IV,LIBRECHAT_JWT_SECRET,LIBRECHAT_JWT_REFRESH_SECRET,LIBRECHAT_ANTHROPIC_API_KEYandLOOM_ASSISTANT_LIBRECHAT_URL=http://librechat:3080to.env, then start theassistantprofile beside the ones the stack runs: an installed stack adds it toCOMPOSE_PROFILESand runsdocker compose up -d; a checkout adds--profile assistantbesidemake dev's--profile bundled(the guide has both commands). LibreChat's model key has a name of its own because theloomservice loads the same.env. On Kubernetes, setassistant.enabledandassistant.existingSecretto a Secret holdingCREDS_KEY,CREDS_IV,JWT_SECRET,JWT_REFRESH_SECRETandANTHROPIC_API_KEY(noLIBRECHAT_prefix); the chart setsLOOM_ASSISTANT_LIBRECHAT_URLitself. - #420: none. A probe that parsed mirror labels out of health should count instead, or run
loom doctoron the host. - #417: a host that shares
var/code_reposwith the containers (the 1.1.0 Writable git mirrors setup) sets the gid when it upgrades: checkgetent group 999holds only the host operator and the containers' user (move the containers to a dedicated gid otherwise), setLOOM_CODE_REPOS_SHARED_GIDin.envto that gid (999 when the check passes), and run, with that gid, thechgrp/chmodin Writable git mirrors again for files an older build created. Once set, restricted mirrors become readable by that group on the first round. The step that setcore.sharedRepositoryper clone is gone. A deployment that runsloom codeonly inside the containers needs nothing. - #421: an auto-registered row written by an earlier build keeps the owner it named, even on a name that already held another identity's data. Review rows tagged
auto-registeredand notadoptedwhoseowner_emailis set on namespaces holding other writers' rows, and re-register any that should not be that owner's as an admin of the name. - #322: an operator whose real delta passes 180 MiB or 6,000,000 counted bytes raises
LOOM_CODE_SCIP_DELTA_FILE_MAX_MBandLOOM_CODE_SCIP_DELTA_MAX_NODES; they apply on the next pass, without a restart. The lanes outside the extractor stamp (SQL, HCL, proto, Jsonnet, Go templates, Kubernetes, TLA+, bats and the other lanes) keep their current rows for a file until it next changes;loom code backfill --paths-filere-reads chosen files now. - #419: none beyond #418's. A deployment whose people should use the assistant needs
LOOM_AUTH_REQUIRED: an answer is delegated from its caller's token. - #431: a native installation reruns
sh scripts/install-code-toolchains.sh. - #424: a code registry row an earlier build writes, before or during the rollout, records no kind. For an operator entry's namespace it is read as the entry's access row, so a namespace a member added from the page (
code.repo.addorcode.repo.add_branch) stays under the entry, with the operator's token and the member's access settings; once the entry leaves the configuration, the row is listed as a branch added from the page. Once the last older replica has stopped, and before removing or moving an operator entry, review each such namespace and the grants copied from it (granted_by = repo-maintainers:<namespace>), as adding repositories says. Before listing a new operator entry, check that its namespace has no directory row yet (namespace.get); one a member registered keeps that member's grants. - #435: on an installation outside the image, the tree-sitter grammars must be installed in the server interpreter's own site-packages, as the image does: the child ignores
PYTHONPATHand the user site, and the server refuses every parse with aLookupError(each file then indexed as chunks without symbols) when it loaded tree-sitter or a grammar from elsewhere. The server image's Python starts the child (sys.executable -I), so it needs no other install. - #451: none; a deployment that bulk-writes into large namespaces may set
LOOM_MEMORY_BULK_PEER_READ=per_fact(performance tuning). - #450: none to upgrade. To use the surface, run
loom sql-access grantas a ClickHouse user with access management. - #467: none for an existing release. A Postgres deployment sets
postgres.*at install, on an empty ClickHouse. - #456: under
LOOM_STORAGE_AUTHORITY=postgres, the Loom role must be able to runpg_logical_emit_message(its text overload; Postgres 17 added a fourth argument with a default). It is executable by PUBLIC on Postgres 16, 17 and 18 unless revoked, and boot refuses a role that may not, naming the grant. - #460: under the Postgres authority, after restoring one store, follow the restore runbook in backup and recovery.
- #483: a deployment on a non-loopback bind with
LOOM_SELF_SIGNUP=open,LOOM_IDENTITY_PROVIDERempty orgateway, and sign-in that is not OIDC stops starting. SetLOOM_IDENTITY_PROVIDER=trust-networkif everyone who can reach the port may have an account, otherwise setLOOM_SELF_SIGNUP=off. - #478: none. A deployment that raised
LOOM_CODE_SCIP_DELTA_FILE_MAX_MBandLOOM_CODE_SCIP_DELTA_MAX_NODESfor memory reasons can now raise them for size alone. - #490: a workers container that runs
LOOM_BACKGROUND_WORKERS=onwrites no lease row, so beside it the web replicas now reportworkers_lease_absentat warn. Switch it toautowithLOOM_DEPLOYMENT=1, as the compose files and the Helm chart already run it. - #531: a trusted caller that acted for a service account's subject must act for the person or bot instead, or call with that service account's own token.
- #497: none until a router is deployed (
loom router migrate, thenloom router serve). A cell needs the Postgres authority and authentication on every transport, so a deployment upgraded from 1.1.0 does not become one; cells has the steps. - #541: once the newer build serves alone, a roll forward after a rollback included, follow upgrade procedure step 13. It lists, with their queries, the active sources on namespaces an older build deleted and the sources registered before a namespace erasure completed that the erasure did not retire, whether an older build ran that erasure or added the source while the newer build's erasure ran, and that were still active when it completed (active now, or retired after it). A namespace erasure an older build started and the newer one finished needs no step: its final census reopens the steps for what the source wrote back. For the first list, remove each source, or delete its namespace again with the newer build; for the second, remove each source with
delete_documents=true(which accepts an already retired source) and delete withmemory.deletethe decision rows mined from its documents, or erase the namespace again only where nobody has written to it since. The second list is a worklist: a removed source stays listed. - #348, #360, #390, #423, erasures: an erasure an older build ran can have left what this release's saga reaches. A user erasure left what a consolidated summary of the user's memories fed (its history, chunks, vectors, graph rows and shared copies) and the cached
memory.askanswers and precedents that quoted it. WithLOOM_CLICKSTACK_DATABASEset, it left the user's model-call spans, which carryloom.user_idorlangfuse.user.id. Against Langfuse 4, a user or namespace erasure that ran the external backends failed atlangfuse.delete_userorlangfuse.delete_namespace(itsfailure_reason), so itsclickstack.*step and the finalverify.residuenever ran. Once the newer build serves alone, and after adding those two keys to an explicitLOOM_CLICKSTACK_USER_ID_KEYSlist, request again, from whatloom.erasure_requestsstill lists (it keeps 90 days), the user erasures, and the namespace erasures that failed atlangfuse.delete_namespace, each with thepurgeit had. A user erasure requested again also erases what the user wrote since, so request one only for a user with no live memory row, or where that is wanted; a namespace erasure requested again erases everything the namespace holds now, so request one only where that is wanted. What an older build's purges left, and the query for the shared copies to review, are in upgrade procedure step 10. - #292, #295, #298, #301, #302, #308: a document extracted before this release keeps its artifact until it is ingested or synced again; a writer re-approves the dataset of a workbook whose header this release renames.
- Settings: every setting this release adds has a default, and none needs setting to upgrade except
LOOM_CODE_REPOS_SHARED_GIDon a host that shares the mirrors (#417),LOOM_MEMORY_CLASSIFY_PRIVATE_HOSTSfor a classifier on a private address (#318), andLOOM_CODE_SCIP_DELTA_FILE_MAX_MBandLOOM_CODE_SCIP_DELTA_MAX_NODESwhere a compiler-tier delta passes their defaults (#322); the configuration reference lists them. - #336: an agent account claimed before this release by signing in with its code no longer signs in with it: its owner claims it again at
/oauth/agent-claimwith the same code, at once, and mints again the tokens minted under it before the claim. - #424, #484: a scheduled
loom coderun whose org has no configured repository entry now exits non-zero: pass the entry's--org, or give that org an entry of its own.loom code scip-lock-pathnow requires--namespace, and a code call orloom codecommand that names no namespace answersDEFAULT_NAMESPACE_REFUSEDwhen a member addedLOOM_CODE_NAMESPACEfrom the page: pass--namespace. - #546: a deployment that records conversation turns from MCP clients with no transcript hook sets
LOOM_MCP_TOOLSET=all, or each such client mounts?toolset=all. - Plugins: the Claude Code and Codex plugins are at 1.2.19; users update them from the marketplace.
What changed for clients since v1.1.0
361 operations served (was 353); ops +8 −0; request fields +3 −0; response fields +19 −0.
New operations
assistant.ask— role VIEWER, budget 30000 ms, MCP extended; request fields: history, namespace, org, questionhelp.get— role VIEWER, budget 20 ms, MCP extended; request fields: anchor, offset, org, path, sectionhelp.search— role VIEWER, budget 50 ms, MCP extended; request fields: limit, org, queryinsights.catalog— role VIEWER, budget 20 ms, MCP extended; request fields: namespace, orginsights.query— role VIEWER, budget 2000 ms, MCP extended; request fields: limit, namespace, org, sqlknowledge.formats— role VIEWER, budget 5500 ms, MCP extended; request fields: namespace, orgknowledge.query— role VIEWER, budget 1500 ms, MCP extended; request fields: aggregates, datasets, group_by, joins, limit, namespace, order_by, org, select, whereknowledge.restore— role WRITER, budget 200 ms, MCP extended; request fields: if_revision, namespace, org, source_uri
Changed operations
code.index_status: response +tier_healthcollection.create: request +derive_name,title; response +titleconversation.append: mcp_tier core → extendedconversation.create: mcp_tier core → extendederasure.export: mcp_tier core → extendederasure.request: response +census,purge; mcp_tier core → extendederasure.status: response +census,purgeknowledge.add: mcp_tier core → extendedknowledge.delete: mcp_tier core → extendedknowledge.get: mcp_tier core → extendedknowledge.list: mcp_tier core → extendedknowledge.search: response +stale_droppedknowledge.source.list: response +accepts_sourcesknowledge.synthesize: request +datasets; response +calculations,clarificationmemory.get: response +last_served_at,served_countmemory.import_evidence: response +uncheckedmemory.list: mcp_tier core → extendedmemory.related: mcp_tier core → extendedmemory.restore: response +redactedmemory.search: response +stale_droppedmemory.search_trace: response +stale_droppedmemory.set_from_messages: mcp_tier core → extendedmemory.sweep_expired: response +moved_skippedmemory.update_by_id: mcp_tier core → extendednamespace.delete: response +sources_retirednamespace.list_mine: mcp_tier core → extendednamespace.restore: response +publication_pendingnamespace.summary: mcp_tier core → extendednamespace.validate: mcp_tier core → extendedprofile.append: mcp_tier core → extendedprofile.set: mcp_tier core → extendedworking_memory.append: mcp_tier core → extendedworking_memory.delete: mcp_tier core → extendedworking_memory.replace: mcp_tier core → extendedworking_memory.set: mcp_tier core → extended
Removed routes
POST /v1/me/setup/mcp_config:POST /v1/me/service_accounts.createmints every token and returns each MCP client's setup (#326).
New Contributors
- @michael-anastasakis made their first contribution in #553
Full Changelog: v1.1.0...v1.2.0