Special Thanks
12.6.0 carries work from a lot of contributors. Thank you to:
- orshih6, who reported GHSA-8p26-g7wp-65jw, the cross-origin and DNS-rebinding hole in unauthenticated local HTTP transports fixed in this release.
- timkjr for
memory oauth list-clientsanddelete-client(#1863) and for the SQLite OAuth store finally enforcing its foreign keys (#1862). - andrunio for rebuilding existing entity graphs (#1860).
- ZYZ666-RGB for marking beliefs and summaries stale when a source memory is deleted (#1865) and the Manage tab translations (#1851).
- samyferhat for the dashboard following the system color scheme (#1793).
- coughlmao for the Russian dashboard translation (#1854) and laozhej for the Korean site introduction (#1866).
- arifrhm for the dashboard restart fix under
python -m uvicorn(#1864). - Yoel-qin, hamid-digitalmarketer, ducanhnguyen223, ppcvote, shu-chen-dot-ca, Khushi6767 and qwist1233-cpu for the HTTP
/mcpshim parity, thememory_graphannotation fix, the dark-mode flash, the versioned-update conflict fix, and a long run of dashboard translation fixes.
Highlights
- Security: unauthenticated local
--streamable-httpand--sseservers refuse cross-origin and DNS-rebinding requests (GHSA-8p26-g7wp-65jw). Upgrade if you run either transport on loopback without an API key or OAuth. Servers with an API key or OAuth keep their current behaviour. memory_quality(action="rebuild_entities")rebuilds existing entity graphs, with a preview by default.memory oauth list-clientsandmemory oauth delete-clientreplace editingoauth.dbby hand.- Deleting a memory no longer leaves beliefs and consolidation summaries citing it.
- Harvest no longer lets an older statement replace a newer memory.
- The dashboard follows the system theme, and adds Russian. The site gains a Korean introduction.
The full entry from CHANGELOG.md follows.
[12.6.0] - 2026-11-05
Added
- Rebuild existing entity graphs with
memory_quality(action="rebuild_entities"), previewing changes by default and applying current extraction settings across all live memories on SQLite, Hybrid, and Milvus. - The dashboard follows the system color scheme by default (#1737). It used to
open in light mode whatever the OS was set to. A newSystemtheme, the default
for anyone who never picked one, tracksprefers-color-schemeand updates live
when the OS switches. An explicit light or dark choice still wins, and the header
toggle keeps flipping between light and dark.theme-init.jsapplies the same rule
before first paint, so following a dark OS does not bring back the flash from #1705. memory oauth list-clientsandmemory oauth delete-client(#1804, timkjr).
Registered OAuth clients, including ones that registered themselves through
Dynamic Client Registration, could only be removed by editingoauth.dbby
hand.list-clientsshows each client without its secret, with its newest
refresh token still on record.delete-clientasks for confirmation (--yes
skips it) and removes the client with its authorization codes and tokens in
one transaction. Access tokens already issued as JWTs stay valid until they
expire, and while registration is open a deleted application can register
again. Both storage backends gainlist_clients()anddelete_client().- Add Russian language support to the web dashboard with a complete translation catalog, automatic language discovery, and regression tests (#1763).
- Added a Korean introduction to the project website, including setup guidance and language navigation.
Fixed
- Prevent a light-theme flash when the dashboard reloads in dark mode (#1705). A synchronous external initializer applies the saved theme before dashboard content is parsed; blocked or malformed storage keeps the default light theme.
- The SQLite OAuth store never enforced its foreign keys (#1804, timkjr).
The schema declaresON DELETE CASCADEfrom authorization codes, access
tokens and refresh tokens tooauth_clients, but the backend never turned on
PRAGMA foreign_keys, so deleting a client left its codes and tokens behind
and a code or token could be stored for a client that did not exist. The
pragma is now set on the connection.store_clientswitched from
INSERT OR REPLACEto an upsert: REPLACE deletes the row first, and with
enforcement on, that delete would have wiped the client's tokens whenever
authenticate_clientupgraded a legacy plaintext secret to a hash. - The Manage tab and the search counts stayed in English in every language (#1808, ZYZ666-RGB).
Tag column headers, Rename and Delete, both “0 results” labels and “0 chunks found”
already had catalog keys, and the page never read them.Filter by type:and
Delete Untaggednamed keys no catalog defined, so the English markup remained.
The tag-statistics error, the untagged-memory sentence and its delete prompts now
come from the catalogs too. A language switch keeps the numbers in those counts.
One search result reads “1 result”, and one document chunk reads “1 chunk found”;
zero and every larger count stay plural. The chosen key stays on the element, so
the next language switch does not put the plural wording back. - The Analytics activity report, activity heatmap and top-tags report stayed English in every language (#1809).
Their labels (Period:,N memories (N%),Often with:,Active Days:, the streaks,Peak Times:,
the heatmap'sN active days,Max: N memories/day, day tooltips,Less/More, and the empty and
load-error messages) were English literals inapp.js. They now come from 18 newanalytics.*keys
in all ten catalogs, and the top-tags period reads as its menu label (Last 30 Days) instead of30d. - The storage report and several Analytics and Quality chart labels were English in every language (#1810).
The storage report's labels, units, date fallback and tag line, the empty and error messages
of the growth, tag usage, memory types and relationship types charts and their aggregate
rows, the quality analytics load error, the score distribution labels and axis title, and the
Local SLM and Implicit Only provider names now come from the catalogs, in all ten languages.
The tags in the storage report are escaped now. - The Knowledge Graph toolbar and its fullscreen view were English in every language (#1811, ppcvote).
The node-limitAlloption, the 3D/2D, rotation, fullscreen, legend and exit tooltips, the
layout group's screen-reader name, the Pause/Play rotation toggle, the node tooltip hints and
the empty and error messages now come from the catalogs, in all ten languages. The rotation
toggle keeps its key, so a language switch shows Play or Pause in the new language. - The memory detail, add and edit dialogs stayed in English in every language (#1815).
Opening a memory showedMemory Details,Created:,Content,TagsandMetadata
in English; editing showedEdit MemoryandUpdate Memory; the delete confirmation,
the save and rating toasts, the active-filter chips, the quality badge tooltip and the
empty states of the recent memories, tag cloud, tag view, search and quality lists did
the same. They now come from the catalogs, with 31 new keys in all ten. Switching to
edit also moves the title's and button'sdata-i18nkey along, so a later language
switch no longer turnsUpdate Memoryback intoSave Memory. - The authentication dialog stayed English in every other language (#1818).
The dialog the dashboard shows when it needs an API key or an OAuth login carried
nodata-i18nattributes at all, so its heading, both section titles, the API key
field, the two buttons and the local-development note kept their English text after
a language switch; the four messages around a login attempt were hardcoded English
strings passed straight toshowToast. The markup now names nine keys
(modal.auth.*, withdata-i18n-htmlfor the note so its<strong>/<code>
markup survives) andapp.jslooks the messages up throughthis.t(); all ten
catalogs carry the thirteen new strings. - The Settings Credentials tab is now translated (#1813). The Cloudflare credentials and sync-owner form, its hints, the connection test and the save messages were English-only in every language. They now read from the catalogs in all ten locales.
tests/web/test_i18n_credentials_tab.pychecks the keys, their placeholders and markup, and that the tab uses them. - Versioned updates no longer flag their parent as a conflict (#1838). They still detect and report conflicts with other memories.
- The HTTP /mcp shim served a weaker protocol than stdio (#1847).
tools/listover HTTP dropped every tool'sannotations(0/26 carried
readOnlyHint/destructiveHint vs 28/28 on stdio),pinganswered
-32601, and thememory_graphschema enum advertised 5 of the 9 actions
the handler accepts —abduct,list_entitiesandentity_profilewere
unreachable for schema-respecting clients. The shim now serializes
annotations, answerspingwith an empty result, the enum lists all nine
actions, and the write-scope exemption derives from a single
GRAPH_ACTIONSconstant so the three lists cannot drift (extract_entities
stays write-only, GHSA-2mw6-h564-86wv behavior unchanged). memory_graphwas advertised as read-only althoughextract_entitieswrites entity links (#1852). The tool annotation now setsreadOnlyHint: Falseon both stdio and the HTTP/mcpshim, so clients no longer auto-approve a write that should require user confirmation.- Harvest no longer lets an older statement replace a newer memory (#1858). A multi-session harvest now processes sessions oldest first, harvested memories record when they were said in
metadata.source_time, and evolution skips a candidate that is older than the memory it matched, storing it separately instead. Opt-in auto-supersede now orders a contradicting pair by that statement time instead of the ingest time. Harvest provenance version is now 4. - Dashboard restart and update no longer crash servers started with
python -m uvicorn(#1861). The delayed restart helper previously rebuilt the command as[sys.executable] + sys.argv. Under-m uvicorn,sys.argv[0]points touvicorn/__main__.py, causing the process to re-execute__main__.pyas a script and placingsite-packages/uvicorn/first onsys.path. uvicorn's internallogging.pythen shadowed standard libraryloggingon startup, crashing with anAttributeErrorcircular import error. The restart command now rebuilds fromsys.orig_argvon Python 3.10+, preserving the original-m uvicorninvocation and interpreter options on both Unix and Windows. - Any web page could drive an unauthenticated local
--streamable-httpor--sseserver (GHSA-8p26-g7wp-65jw, orshih6).
With no API key or OAuth on a loopback bind, the/mcppreflight answered every origin
withAccess-Control-Allow-Origin: *, and neither transport checkedHostorOrigin,
so a cross-origin page could callmemory_deleteor plant memories, and a DNS-rebinding
domain could also read the results. An unauthenticated server now only accepts loopback
Hostvalues and loopback origins plusMCP_CORS_ORIGINS(403/421 otherwise), and the
preflight grants exactly those origins. Servers with an API key or OAuth keep their
current behaviour, including behind a reverse proxy. - Deleting a memory left beliefs and consolidation summaries citing it (#1857, ZYZ666-RGB).
Graph edges were removed in the delete transaction, butderived_from,
contradicted_byandmetadata.merged_from_hasheskept the hash, and
compression and association memories kept it insource_memory_hashes.
Retention deletes this way all the time, soget_beliefskept returning a
belief whose evidence was gone. The same transaction now drops the hash from
those lists, recordsmetadata.stale_sources(how many, and when), and marks
a beliefungroundedwhen nothing is left. That status is notsuperseded.
Confidence is unchanged until the nextchallenge_belief. Summaries are
flagged and not rewritten. A later re-derivation does not union a merged
source that is no longer live. Re-deriving an ungrounded belief promotes it
back to candidate, or to active when the usual floors are met. A tombstone
applied through delta sync runs the same cleanup. A summary flag advances
updated_atand records anupdate_metadataevent when the sync log is on.
If that cleanup fails, the delta-sync apply rolls the tombstone and the
event back, so a retry can finish instead of sticking as a duplicate. A
local delete rolls that same transaction back before it releases the
connection lock. Re-derivation writes only sources that are still live,
and saves that list with confidence and status together. - Five localized website introductions still allowed Python 3.10 (#1868). The
German, Spanish, French, Japanese and Simplified Chinese pages said Python 3.10 or
newer was enough after #1770 had retired 3.10. They now say 3.11, like the English,
Brazilian Portuguese, Traditional Chinese and Korean pages.
Internal
- Explain the optional
sqlite_vecimport guard instorage/mixins/retrieve.py, clearing CodeQLpy/empty-exceptalert 639 (#1867).