Releases: nitekeeper/memex
v2.18.0
v2.18.0 — 2026-06-27
Changed — ~48% fewer tokens loaded per memex:run (behavior-preserving)
A token-footprint optimization across the three tiers that enter a context
window, with no change to what any skill does (one bounded, documented exception
below). Measured with a fixed tokenizer:
- Always-loaded routing skill −47.7% (
skills/run/SKILL.md, paid on every
memex:run): the Step 0 cold path — platform-specific Python-install blocks,
the plugin-root discovery cascade, and the bootstrap consent/error prompts —
moves to a newskills/run/STEP0.md, read on demand only when a preflight
check actually fails. On a healthy install it is never loaded. The happy-path
checks (Python probe,config.jsonplugin-root read, five-path existence) and
the failure-branch semantics stay in the always-loaded skill; every verbatim
install/bootstrap/error string survives byte-for-byte inSTEP0.md. - Per-operation cost −35% to −41%: per-procedure prose trims, and the
per-tier model-rationale blockquotes collapse to one-line pointers (the
canonical ENFORCED table already lives inCLAUDE.md). The repeated
EmbeddingUnavailabletry/except is centralized into
embeddings.encode_or_skip(). - Per-dispatch −30% on the two LLM-dispatched agent profiles, plus compacted
in-prompt JSON (separators=(",", ":")) in the Librarian and Community-Reporter
prompts.
Changed — bounded large-document recall trade (the one behavior delta)
The Librarian classification window (build_prompt char_budget) drops 80000 →
40000 chars and the synthesize source budget drops 50000 → 32000. Documents
longer than the window are classified/synthesized on a head window — already
surfaced honestly via the existing body_truncated / truncated markers; the
citation graph is unaffected (the synthesizes / relation edges are added
independently of the prompt body). Anti-revert test ceilings were tightened in
lockstep.
Internal
tests/test_skill_run_preflight.pyrepointed to assert the moved Step 0
verbatim blocks againstSTEP0.md(the verbatim-survival guard follows the
text); addedtests/test_embeddings.pycoverage forencode_or_skip.- Deterministic-agent seed profiles (Archivist, DBA, Data Steward) trimmed for
symmetry with the Librarian / Reference-Librarian profiles. - Shipped via a 3-cycle kaizen run (#15); no
model:tier line, the M3
single-write-path, or any untrusted-input guard was altered.
v2.17.0
v2.17.0 — 2026-06-27
Added — dashboard search now spans agents + the code graph (grouped)
Dashboard keyword search previously covered only the federated document index, so
the agents registry and the code-navigation graph were unsearchable. Search now
spans three surfaces, grouped in the results list (Documents / Agents / Code):
- documents — the federated index (FTS5 + LIKE fallback), unchanged.
- agents — the roles/agents registry (
agents.db) by name / description / profile. - code — the code-navigation graph (
code_graph.db) by node label, per repo.
Each result carries a kind and a prefixed id; build_doc() is now a dispatcher
(role:/agent: → the registry profile, code:<repo>|<node> → a code-graph node
summary of its location + callers / outbound calls-uses, otherwise → a
federated-index document). All read-only. The agent profile and code-node summary
render through the existing XSS-safe Markdown renderer, with every embedded value
(labels, file paths, repo slugs) backtick-wrapped so an adversarial value cannot
form a live link.
Per-surface result caps (documents 50; agents + code 20 each, with roles and
agents given independent budgets so neither starves the other) and per-surface
truncation (truncated_kinds) so a "+" badge only appears on a group that
actually overflowed. Code-node caller/uses sections show a true "N of M" when
capped, and outbound edges to un-ingested target nodes still appear. New SQL is
fully parameterized and LIKE-escaped.
24 new/updated tests in tests/test_dashboard.py cover agent + code search and
detail, the id-prefix dispatch and its error branches, injection / wildcard
safety on the new surfaces, missing-store degradation, the node-id-with-pipe
round-trip, the dangling-target fallback, and the per-surface truncation badge;
the existing document-search assertions are scoped to kind=="document".
v2.16.0
v2.16.0 — 2026-06-26
Added — document content renders as Markdown; search dismisses on outside-click
The content overlay (opened from the dashboard search list and from a 3D-graph
node click) now renders Markdown instead of showing raw text: headings,
bold/italic, inline and fenced code, lists, blockquotes, horizontal rules, links,
GFM pipe tables, and leading YAML frontmatter. A raw/rendered toggle is
available in the overlay header.
Because the content is an ingested (potentially adversarial) document body and
the page runs under a strict default-src 'none' CSP, the renderer is
dependency-free and XSS-safe by construction: it builds DOM nodes directly
(createElement + textContent + createTextNode), with a link-href allow-list
(http/https/mailto/relative/anchor; javascript:/data: reject to inert
text) and a whitelist of element types. Raw HTML in the body is never parsed — it
lands as inert text — so it can never become script, and no innerHTML of
document text exists anywhere. It is DoS-guarded too: the inline parser falls back
to plain text above 20k characters (anti quadratic-regex ReDoS) and blockquote
recursion is depth-capped.
Search results now collapse on an outside click or Escape (previously the
collapse path removed only the panel chrome because a base display:none rule was
missing) and re-open on focus when a query is present.
22 new/updated tests in tests/test_dashboard.py pin the renderer's safety
contract — that it builds the DOM (no innerHTML of data, tightened to require
the exact empty-string clear), that the anchor href comes from the sanitizer, that
the ReDoS + recursion caps remain, and that the search-collapse wiring and base
hide rule are present.
v2.15.0
v2.15.0 — 2026-06-26
Added — keyword document search + click-to-read content overlay
The dashboard gains a keyword search box: type a term and see the matching
documents (FTS5-ranked over the federated index, with highlighted snippets and a
LIKE fallback). Click a result — or click a node in the 3D graph — to read
that document's full content in a shared overlay.
build_search(q) searches documents_fts with a prefix-tokenized, bound MATCH
(no FTS5 query-syntax injection) and an escaped LIKE fallback. build_doc(id)
returns a document's full record plus best-effort content fetched from its
source store, joined by index_id (the universal join key; falls back to
id=row_id, then to the indexed searchable text), capped at 500k characters
with a content_truncated flag. Two new read-only routes back these:
GET /api/search?q= and GET /api/doc?id=. The shared _send_json helper now
returns a generic {"error":"internal error"} and logs detail to stderr only, so
a --allow-non-local bind can't echo internal paths to the network.
The content overlay (CSS/markup/JS) is injected into both the dashboard and
the 3D graph. openDoc(id) renders title/meta/body via textContent only — an
ingested document title or body containing markup can never become script — with
focus management, Tab/Escape handling, and a truncation note. The dashboard adds
a debounced search box + results list (rendered via el()); the 3D graph opens
the same overlay on node click. Deep links: ?q=<keyword> prefills and runs the
search, ?doc=<index_id> opens a document. Read-only throughout — the search and
viewer write nothing, so they are not a Librarian/M3 write-path bypass.
21 new/updated tests in tests/test_dashboard.py cover search matching,
wildcard-escaping and the forced LIKE fallback, injection-safety, content
fetch via index_id-join / id-join fallback / multi-column assembly /
searchable fallback / no-content / the size cap, the two routes, and a
regression guard that the overlay markup precedes the inline script on both pages.
v2.14.0
v2.14.0 — 2026-06-26
Added — interactive 3D knowledge-graph view (/graph)
The dashboard gains an Obsidian-style knowledge graph, orbitable in 3D,
reached from a "◉ 3D graph" link on the dashboard or directly at /graph.
Documents are nodes, relations are edges, colored by GraphRAG community; drag to
orbit, scroll/pinch to zoom, hover for a tooltip, click a node to focus its
neighborhood, search to filter.
build_graph() (in scripts/dashboard.py) opens index.db read-only and
returns {nodes, links, truncated}: links are kept only when both endpoints are
in the node set (dangling and self-loops dropped), community color comes from
community_members, truncation is deterministic (ORDER BY created_at, index_id)
and capped at 900 nodes (surfaced via the truncated flag). New routes
GET /graph and GET /api/graph, plus a _send_json() handler helper backing
both JSON endpoints.
The viewer is a self-contained vanilla-JS 3D force simulation rendered to a 2D
canvas with perspective projection + camera orbit — no Three.js / WebGL / CDN,
so it stays under the page's default-src 'none' CSP and ships no vendored
blobs. Physics work is bounded by a fixed pair-op budget so load and settling
stay smooth up to the node cap. Every DB-derived string (label, domain,
community, tooltip, legend) renders via textContent / canvas fillText, so an
ingested title containing markup can never become script. Read-only throughout —
the viewer writes nothing, so it is not a Librarian/M3 write-path bypass.
11 new tests in tests/test_dashboard.py cover the projection (structure,
dangling/self-loop drop, truncation boundary, community color, corrupt-index
degradation, bootstrap guard, _node_label fallbacks), a data-as-data
(no-XSS) guard over the viewer, and the two new routes.
v2.13.0
v2.13.0 — 2026-06-26
Added — Memex dashboard: a local, read-only overview of everything stored
New memex:run intent — "show me a dashboard of what's in Memex" — routed to
internal/steward/dashboard/SKILL.md (memex:steward:dashboard). It launches a
self-contained web dashboard summarizing every Memex store at a glance.
scripts/dashboard.py is the deterministic backing (stdlib only: http.server,
sqlite3, and json — no third-party deps). It opens every registered store
plus the fixed-path code_graph.db read-only (SQLite mode=ro) and aggregates a
cross-store summary: per-store row counts, the federated index (documents by
domain / source store / author, relations by type, embedding coverage, an
ingestion timeline), GraphRAG knowledge communities with ratings, Brain captures,
the code-navigation graph (per-repo nodes/edges + freshness), and the agent
registry. It is served as a single HTML page plus a live /api/summary JSON
endpoint and a /healthz probe.
It writes to nothing, so it is not a Librarian write-path bypass (spec §6 /
M3 govern document writes; this is pure read-side reporting). Safety contract:
binds 127.0.0.1 only and refuses a non-local bind without --allow-non-local
(a blank --host normalizes to loopback, never 0.0.0.0); serves only three
fixed routes (no filesystem handler, so no path-traversal surface); renders all
DB-derived strings client-side via textContent under a default-src 'none'
CSP (no XSS from ingested titles); and degrades gracefully when a store, table,
or column is missing or a store file is corrupt. A --once flag prints the
summary as JSON for headless/SSH use.
The internal-procedure count rises 30 → 31; routing, README, USER_GUIDE, and the
procedure-count tests are updated to match. 20 new tests in
tests/test_dashboard.py cover the aggregator's defensive degradation, the
read-only guarantee, the loopback-bind guard, the port-scan, and the HTTP routes.
v2.12.2
v2.12.2 — 2026-06-19
Fixed — migration runner self-heals a ledger/schema desync
The Memex Core-store migration runner (scripts/stores.py) no longer crashes
with duplicate column name / table already exists when a store's
migrations ledger has fallen behind its actual schema. This was the sibling
defect to the atelier migrate.py fix — atelier runs in Memex mode and shares
the migration set into ~/.memex/atelier.db through this runner, so a ledger
desync here broke /atelier:save.
Each migration is now applied per-statement (split via stdlib
sqlite3.complete_statement() — tokenizer-aware over string literals, comments,
and trigger BEGIN…END/CASE…END bodies) inside a SAVEPOINT. An already-exists
OperationalError (anchored classifier) is treated as already-applied and the
file is recorded so the ledger self-heals; every other error rolls the file back
(no half-applied-then-recorded hazard) and propagates. File body + ledger INSERT
commit together on RELEASE (schema/ledger lock-step, no desync window), and the
connection is always closed on the error path.
v2.12.1
v2.12.1 — 2026-06-13
Fixed — python -m scripts.agents CLI regression + stale doc paths
The python -m scripts.agents command-line entrypoint is restored. It had
silently regressed when scripts/agents.py was refactored into the
scripts/agents/ package: the package had no __main__, so python -m scripts.agents no longer ran. The invocation works again, and a regression
test now guards it.
scripts/agents/__main__.py— new module sopython -m scripts.agents
works again; it delegates to an extractedmain()in
scripts/agents/__init__.py.internal/core/get-agent/SKILL.md,internal/core/register-agent/SKILL.md
— corrected stalescripts/agents.pyinvocation paths to
scripts/agents/__init__.pyto match the package layout.tests/test_agents_cli_smoke.py— new smoke test covering the
python -m scripts.agentsCLI so the regression cannot recur.
v2.12.0
v2.12.0 — 2026-06-12
Removed — settings-recommendation-on-upgrade (consent-gated)
The version-upgrade settings recommendation shipped in v2.11.0 is removed:
recommending Claude Code model/cost settings is out of memex's
persistent-memory scope. The canonical implementation lives in the atelier
plugin (scripts/recommended_settings.py +
internal/settings-recommendation/SKILL.md, surfaced via startup_check()'s
settings_rec_offer; atelier PR #109), which fully covers the same behavior
(same RECOMMENDED keys/values, y/N default-No consent, merge-safe atomic
apply, once-per-version state).
- Deleted
scripts/recommended_settings.py,
internal/core/settings-recommendation/SKILL.md, and
tests/test_recommended_settings.py(plus the conftesttmp_settings_path
fixture). skills/run/SKILL.md: Step 0.3 removed — Step 0.2's success path proceeds
straight to routing; the Core CRUD routing row for the offer is gone.CLAUDE.md: the "Settings-recommendation-on-upgrade" section removed, and
the pre-existing advisory "Model recommendations" section (Opus-high default
posture + per-skill/agent override table) removed for the same reason —
memex does not recommend model/cost settings, advisory docs included. The
CI-guarded "Dispatched-subagent tiers (ENFORCED)" per-dispatch cost floor is
unchanged (it is an enforced dispatch policy, not a settings recommendation).- A leftover
~/.memex/settings_rec_state.jsonmarker on upgraded installs is
inert debris; nothing reads it.
v2.11.0
v2.11.0 — 2026-06-07
Added — settings-recommendation-on-upgrade (consent-gated)
When the memex plugin version is bumped, the first memex:run on the new
version OFFERS the user (y/N, default No, once per version) to apply the
cost-optimized recommended settings to ~/.claude/settings.json. On consent the
settings are MERGED in — merge-safe, never clobbering existing keys, never
touching managed-settings.json. Mirror of atelier PR #109, adapted to memex.
scripts/recommended_settings.py— the canonical single source of truth.
RECOMMENDED = {model: "sonnet", effortLevel: "high", autoCompactEnabled: true}
withmodelas the family aliassonnet(NOT a pinnedclaude-sonnet-*
id). Read-only compute paths (settings_path,state_path,
current_plugin_version,load_settings,compute_changes,read_state,
eligibility) never write; onlyapply_recommended(merge-safe, atomic
temp-file +os.replace, mkdir-parent) andwrite_state(per-version
marker under memex home) mutate. Every path is a graceful no-op on error so
it can never crash a memex invocation. Astatus/applyCLI guard mirrors
onboarding.py/install.pyfor manual recovery.internal/core/settings-recommendation/SKILL.md— the human-facing
consent surface: the y/N (default No) prompt + the managed-settings caveat.
Python computes/applies; the SKILL asks.skills/run/SKILL.mdStep 0.3 — a read-only post-bootstrap eligibility
check wired into the real startup path BEFORE routing, plus a Core CRUD
routing row so the procedure is discoverable. Detection is read-only; only the
consent path writes.- M3 distinction.
~/.claude/settings.jsonis a LOCAL Claude Code config
file, NOT a memex-managed store, so M3 (all writes through the Librarian) does
NOT apply — the write goes directly, never via the Librarian / Archivist /
Memex Core. Documented inCLAUDE.md§ Model recommendations. - Tests —
tests/test_recommended_settings.py(hermetic; conftest
tmp_settings_pathfixture + env overrides) covers constant-pin (sonnet
alias), merge-safety, idempotency, version-gating, missing/malformed graceful,
read-only-compute-never-writes, atomic-no-debris, startup-wiring, consent-SKILL
presence/frontmatter, CLAUDE.md doc-pin, and version-agreement.