Repository navigation
Releases: ali-ulu/levh
Releases · ali-ulu/levh
Release list
v2.35.0
Upgrade notes
- Windows users: upgrade.
levh servecould stay running while refusing every connection after one client dropped mid-accept (WinError 64), so every agent sawECONNREFUSEDuntil a manual restart. Fixed below;uvicorn>=0.36is now required. - Removed: the demo-data feature —
levh seed-demo,levh remove-demo,setup --demo/--real,POST /api/seed-demoandPOST /api/onboarding/remove-demo. Scripts that call them must drop those calls;levh setupnow always prepares an empty store. - New, opt-in:
python -m server.supervisorkeeps a git-checkout install running and fast-forwards it tomainevery 6 hours (LEVH_AUTO_UPDATE_HOURS,0turns it off). Nothing changes unless you start it.
feat: levh update and a supervisor that keeps the server up and current
levh update [--check] [--remote origin] [--branch main] [--json]fast-forwards the git checkout LEVH runs from. It refuses a tree with local edits to tracked files, another branch, a history upstream does not contain, or a directory that is not the checkout root (a wheel install nested in a versioned home directory is never "updated"). Whenpyproject.tomloruv.lockchanged it reinstalls the checkout (pip, oruv pipin a uv environment without pip) and rolls back withgit reset --keepif that fails. Git runs with prompts disabled and every call has a deadline that kills the whole process tree, so an unattended run cannot hang on a credential prompt.python -m server.supervisorrunslevh serveas its child and restarts it when it exits (with crash-loop backoff), when the port actively refuses while the process lives (the Windows listener loss), or after ten failed health probes past a ten-minute boot grace. It adopts, and never kills, a server it did not start. A lock file keeps one copy, so a logon task can launch it every few minutes. EveryLEVH_AUTO_UPDATE_HOURS(default 6,0off) it checks for an update and, only when behind, stops the server, updates, and starts it again. Health probes go to loopback for wildcard binds and bypass any HTTP proxy. Seedocs/installation.md.
fix: keep the Windows listener alive after a client drops mid-accept
- On Windows
levh serveran on uvicorn's defaultProactorEventLoop, whose accept loop closes the listening socket on the firstOSErrorfromAcceptEx.WinError 64("the specified network name is no longer available") is raised whenever a client resets its connection before it is accepted — routine for MCP clients that time out and retry — so after one such drop the process kept running but refused every connection, and every agent sawECONNREFUSEDuntil someone restarted it.levh servenow passes uvicorn aSelectorEventLoopfactory on Windows (server/core/event_loop.py); other platforms keep uvicorn's default. Observed on CPython 3.12.10 in production (Accept failed on a socket ... WinError 64in the server log, then no listener) and reproduced on 3.13.13: 20 reset connections kill the proactor listener, the selector listener keeps serving.tests/test_serve_event_loop.pyreplays that scenario on both loops (the proactor half only on Windows, as a canary for a CPython fix). - The
uvicornfloor rises from>=0.30to>=0.36: custom loop factories (Config.get_loop_factory) arrived in 0.36, and an older uvicorn would fail to startlevh serveon Windows with this change. - Running
uvicorn server.api:appby hand on Windows still gets the proactor loop; pass--loop server.core.event_loop:selector_event_loop.
feat: remove the demo-data feature
- Dropped the demo corpus and its whole surface:
server/core/demo_data.py,MemoryEngine.seed_demo,MemoryOnboardingMixin's demo helpers,POST /api/seed-demo,POST /api/onboarding/remove-demo, thelevh seed-demo/levh remove-democommands,setup --demo/--real(setup now always prepares an empty store), theseed_demo_completeddogfood event, and the dashboard's "Load demo data" / "Remove demo data" onboarding buttons. Onboarding readiness is now a single path: store a real memory and connect an MCP client. - The read-only public-instance boundary (
LEVH_PUBLIC_DEMO) is unrelated and unchanged.
fix: keep the memory drawer and the quick-add FAB clickable
- The memory detail drawer rendered inside
<main class="relative z-10">, a stacking context that sits below the sticky header (z-30). Its ownz-50could not escape it, so the header — and its Quick-capture link — painted over the drawer and swallowed clicks on Pin, Delete and Close. It is now portalled todocument.body, which is what makes those controls reachable;memory-detail-drawer.test.tsxasserts the portal so a future edit cannot quietly drop it. - The librarian widget's FAB is
position: fixedwithz-index: 99999and was pinned to the same corner as the dashboard's quick-add FAB. Every click on "Add new memory" landed on the librarian button and opened its chat instead, so the quick-capture dialog never appeared. The widget FAB now sits above the quick-add one (bottom: 96px,80pxon narrow screens), andbuttons.spec.tsfails if the two overlap again.
perf: stop the model-free interference scan from freezing the event loop (#505)
- In
hashembedder mode_interference_candidatesscores a new memory against every stored one, and each comparison scanned the full content vocabulary of both texts with_stem_matches. On a store of ~500 source documents (avg 16 KB) onestore()spent 47 s on the event loop, so an auto-feed ingest pinned a CPU core and the dashboard stopped answering.similarity_expandednow indexes the content words by their_MIN_STEM_LENGTHopening prefix, so a term only compares against the words that can actually share its stem. The filter is exact — every comparison the flat scan would have made still runs — and a full scan drops from ~4.2 s to 0.09 s (~43×). Scores are bit-identical: differential fuzzing over 10 000 random pairs (Turkish text, short tokens, random synonym expansions) found no difference.
fix: serve the complete dashboard export, not a stale bundle-less one (#507)
_dashboard_diraccepted any candidate with anindex.htmland an_next/staticdirectory. A checkout that built the export once keeps that_nextbeside newer committed HTML, so the referenced content hashes are gone:/answered 200 while the chunks 404'd and React never hydrated, and routes whose chunk was missing rendered "Something went wrong" — the dashboard's buttons did nothing because the page never came up._missing_dashboard_assetsnow reads every HTML file in a candidate and confirms each referenced_next/staticbundle is present; only a complete export is chosen, and the packagedserver/dashboard(which always ships its bundles) is served instead of the partial one.frontend/e2e/start-server.shruns the same check before deciding whether to rebuild.
fix: stop the conflict list from 500ing on a concurrent rebuild
insert_conflict_if_absentnow usesINSERT OR IGNOREinstead of a read-then-insert. Two freshness reads can each trigger a derived-state rebuild at once (only the background pass is coalesced), and the check-then-insert race let both see a candidate pair as absent and then collide on its primary key. The loser raisedUNIQUE constraint failed: memory_conflict_candidates.id, turning/api/conflictsinto a 500 that the dashboard's Conflicts page hit on load. The observable contract is unchanged: an existing row — any status — is left untouched.
fix: make the dashboard's Copy buttons work outside localhost
- Added
copyText(frontend/src/lib/clipboard.ts): it prefersnavigator.clipboard.writeTextand falls back to a hidden-textareaexecCommand("copy")when that API is missing (a plain-HTTP LAN origin) or rejects the write (NotAllowedError). The Settings, Projects and Onboarding copy buttons now report success truthfully instead of throwing and silently doing nothing.
fix: serialize explicit write transactions against a derived-state rebuild
- Every write path shares one aiosqlite connection, and a derived-state rebuild pass holds an implicit transaction open between its first
DELETEand its commit. A hard-delete or a snapshot restore issuingBEGIN IMMEDIATEin that window raisedsqlite3.OperationalError: cannot start a transaction within a transaction. The startup warm task added in #503 made a background pass overlap a request's transaction routine, so the failure moved from rare to about one run in three.Database.transactionnow holds awrite_lockacross an explicit transaction, and the rebuild holds the same lock for its whole pass, turning the interleave from a race into an invariant. The retry backoff sleep stays outside the lock, so a failing pass cannot hold writers off for the backoff duration.
fix: stop a pre-restore safety backup from deadlocking a derived-state rebuild
create_safety_backupnow takeswrite_lockfor the duration of the SQLite online backup.Connection.backupis queued on the shared connection like any other statement, but its worker-thread call retries in 250 ms sleeps until SQLite reports it done, so it can outlive the statements queued around it. A backup queued while a rebuild heldwrite_locksat ahead of the rebuild's own queued commit, and neither could run:/api/restorewedged forever with no error and no timeout. Areplacerestore takes the backup on its way into the same transaction, so the two are exactly the paths that meet.
v2.34.0
federation: bring the dashboard into the pull-first transport (#498)
- Added
POST /api/v1/federation/pull— the server-side twin of thefederation-pullCLI command. Fetches one signed envelope from an explicit peer URL, verifies it locally withLEVH_FEDERATION_KEY_PATH(the key never leaves the server), and admits its memories through the existing admission gate. CLI security contracts carried over verbatim: token auth requires HTTPS on remote peers, no redirects, optionalexpected_node_idpin, verification completes before any write. - Added a Settings → Federation dashboard card: peer URL + optional expected-node inputs, a pull button, verified-envelope provenance and the admission-gate counter grid (imported / redacted / duplicates / held / errors). Full TR/EN i18n.
- The dashboard endpoint builds its request URL only from an operator-approved
LEVH_FEDERATION_PEERSallowlist — a browser session can never point this server at an arbitrary host (SSRF boundary, CodeQL-flagged and fixed before merge). - Documented the new route in the API reference so the docs-match-code guard stays green.
chore: prune unused frontend components and stale automation prompts (#496, #497)
- Removed five dead frontend components (fading-memories, memory-result-card, three unused ui primitives) and two stale LEVH automation prompt files; no behavior change.
v2.33.0
fix: anchor the antonym conflict signal to a shared topic
- Bare antonym words ("keep" in one memory, "drop" in another) no longer open a conflict candidate on their own: the antonym signal now also requires a shared topic term in both texts, mirroring the decision-conflict lane.
- Same-subject opposition ("keep the cache table" vs "drop the cache table") still fires; unrelated passages stay silent.
- The next conflict rescan automatically prunes open noise rows that no longer fire; reviewed verdicts are never reset.
federation: add explicit pull-first peer transport (#484)
- Added a source endpoint that serves the existing signed B0 federation envelope using configured node identity/signing key and the normal API authentication boundary.
- Added
levh federation-pullfor explicit receiver-initiated exchange: optional token-file authentication, no redirect following, HTTPS required when sending a token to a remote peer, signature/node verification before opening the local store, and admission-gated import. - Kept federation transport deliberately narrow: no background polling, push, CRDT/shared store, or automatic conflict merge.
federation: prove cross-instance recall quality (#488)
- Added a two-instance quality proof: a signed peer envelope imported through the verified boundary is recallable on the receiver, decays on the receiver's clock (pin cleared, counters reset, access time re-stamped), and never displaces a pinned local memory.
- Exposed the proof as a content-free
federationsection in the offline evaluation report (levh eval run).
feat: add the memory assistant chat
- Added
/assistant, an embedded Opengeni chat that answers from the local store: the proxy recalls memories for each message, attaches them as model context, and the agent cites what it used. - Implemented Opengeni's session-proxy contract for the FastAPI backend so the organization key stays server-side and the browser only talks to
/api/opengeni/*. - Chats use an installation-scoped upstream identity, the agent has no workspace tools, and recall is read-only with
reinforce=False. - Forwarded only the conversation surface and kept uploads, artifacts, voice, model selection, forensic reads, and cancel disabled.
- The assistant stays off until
LEVH_OPENGENI_API_KEYis configured; its UI copy now ships in both English and Turkish.
continuity: lock client delivery channels to runtime registries (#486)
- Added a code-backed client × channel matrix for stderr emission, native session-start hooks, and the MCP continuity tool/start directive.
- Added a drift test that derives the matrix from the MCP platform registry, native hook registry, minimal tool tier, and shared MCP instruction.
- Corrected README claims so only Claude Code is described as having an installer-owned native SessionStart hook; stderr emission is no longer presented as proof of client consumption.
i18n: ship Turkish UI locale and switcher (#481)
- Added a complete Turkish catalogue in
frontend/src/lib/i18n/tr.json, keptenas the default/fallback locale, and added parity coverage so both catalogues carry the same keys and interpolation variables. - Added an English/Türkçe header switcher backed by
localStoragekeylevh_locale; the selection survives reloads and updates the document language without adding locale route segments or request-time negotiation. - Kept the static export architecture unchanged and added unit/E2E coverage for Turkish translation, fallback, persistence, and reload behavior.
docs: reconcile frontend i18n rollout status (#479)
- Updated the internal roadmap to record that English-copy extraction is complete through #478 while the multi-language report item remains open because only
enships. - Updated the i18n design record so the sequencing/status text matches the completed surface rollout and points the next product step at a second locale plus switcher/persistence.
i18n: make build-time metadata server-safe (#477)
- Split pure catalogue translation into
src/lib/i18n/translate.tsso build-time/server code can translate without importing the client hook. - Kept
@/lib/i18ncompatible by re-exportingtranslateandDEFAULT_LOCALE, whileuseT()remains client-only. - Routed root layout title, description, and application name through the catalogue; its UI-string ratchet drops from 4 to 1, leaving only the technical
afterInteractiveScript strategy token. - Added coverage for the server-safe metadata path and updated the i18n design record.
i18n: move Memory Detail Drawer copy into the catalogue (#475)
- Drawer actions/accessibility, trust, retention, metadata, score-breakdown, chart-tooltip, formula, and related-memory copy now resolve through
useT(). - Dynamic memory/trust/score/relationship values remain runtime data; the UI-string ratchet for
src/components/memory-detail-drawer.tsxdrops from 75 to 11, leaving only technical locale/date-format, keyboard-navigation, and chart/style detector residuals.
i18n: move onboarding empty-state copy into the catalogue (#473)
- Onboarding setup, demo, real-memory, MCP, dogfood, readiness, terminal, status, confirmation, label, and accessibility copy now resolves through
useT(). - Dynamic server values remain runtime data; command snippets remain technical literals. The UI-string ratchet for
src/components/onboarding-empty-state.tsxis provisionally tightened from 44 to 2.
i18n: move the Projects page into the catalogue (#471)
- Projects page/empty-state/dialog/action/help/error/count/date copy now resolves through
useT(). - Project names, context filenames/content, format names, internal sentinels, locale/key identifiers, MIME values and CLI/MCP commands remain runtime/technical data; the UI-string ratchet for
src/app/projects/page.tsxdrops from 43 to 7, leaving only technical/internal detector residuals.
i18n: move the Visualize insights page into the catalogue (#469)
- Insights/trust copy, chart titles/subtitles, chart series display names, empty/loading states, memory layer labels, durability bucket labels, and project/date fallbacks now resolve through
useT(). - API values, trust enum values, chart field keys, and Recharts configuration remain runtime/technical data. The UI-string ratchet for
src/app/visualize/page.tsxdrops from 39 to 12, leaving only Recharts/style/configuration detector false positives.
i18n: move the Home dashboard into the catalogue (#467)
- Home hero/status copy, metric cards, recent-memory labels, knowledge/network panels, briefing copy, conflict/review signals, empty states, and relative-time copy now resolve through
useT(). - Memory/entity/project/source/status values and API data remain runtime data; locale/formatting enum tokens remain technical. The UI-string ratchet for
src/app/page.tsxdrops from 39 to 0.
i18n: move the Memories page into the catalogue (#465)
- Memories page/search/filter/empty-state/edit-dialog/action/relative-time/delete-confirm copy now resolves through
useT(). - Memory/project/source/tag/session values and enum values remain runtime data; locale and non-UI diagnostics remain technical. The UI-string ratchet drops from 38 to 3, leaving only technical locale/diagnostic/class-token residuals.
i18n: move Connectors settings into the catalogue (#463)
- Connector descriptions/categories, server-offline guidance, upload/import/sync result copy, field/action labels, and sync-history copy now resolve through
useT(). - Connector ids, config keys, path/file examples, commands, job/status enums, locale ids, and API values remain technical/runtime data; the UI-string ratchet entry for
src/app/settings/_sections/connectors.tsxdrops from 34 to 2, leaving only the technicallevh servecommand anden-GBlocale literal.
i18n: move Memory Quick Add into the catalogue (#461)
- Memory Quick Add template labels/prompts/examples, dialog/accessibility copy, capture fields, pin/action copy, and fallback error now resolve through
useT(). - Template ids/tags, source/memory-type values, hash target, and keyboard event keys remain technical/runtime data; the UI-string ratchet for
src/components/memory-quick-add.tsxis provisionally tightened from 30 to 2 for the twoEnterkey literals.
i18n: move the Mistake Guard page into the catalogue (#459)
- Guard heading/subtitle, refresh/error/empty states, rule/incident headings and counts, task labels, severity display/filter labels, empty-filter copy, and table headings now resolve through
useT(). record_mistakeremains a technical tool identifier and rule/violation/project/source/tool/date values remain runtime data; the UI-string ratchet entry forsrc/app/guard/page.tsxdrops from 20 to 0.
i18n: move the Agents page into the catalogue (#457)
- Agents loading/headings, stat labels/help, relative-time copy, breakdown counts, activity/checkpoint empty states, project/connectivity/status copy, and connection/session counts now resolve through
useT(). - Agent names, API routes, icon-map keys, project/checkpoint/runtime values remain technical/runtime data; the UI-string ratchet for
src/app/agents/page.tsxis provisionally tightened from 20 to 1 for the non-UI console diagnostic.
i18n: move the People page into the catalogue (#455)
- People list/detail headings, summaries, search, empty-state guidance, memory counts, last-seen copy, ask action/fallback, and detail headings now resolve through
useT(). - Dynamic person/memory/email/date/source values remain runtime data; the UI-string ratchet for
src/app/people/page.tsxis tightened from 19 to 5, leaving three connector identifiers and two source-normalization literals.
i18n: move the Findings page into the catalogue (#453)
- Findings tabs, heading/subtitle, refresh/empty state, occurrence/timeline/note copy, note placeholder, and review/delete actions now resolve through `...
v2.32.0
Frontend toolchain: Tailwind v4, ESLint 9, Next 16 (#188, #186, #189)
- Tailwind CSS upgraded to v4: PostCSS plugin moved to
@tailwindcss/postcss,
@tailwinddirectives replaced with@import "tailwindcss"+@configto
preserve the existing shadcn-style TS theme;autoprefixerdropped (built-in). - ESLint upgraded to v9 with
eslint-config-nextv16; legacy.eslintrc.json
replaced by flateslint.config.mjs. The one genuinereact-hooks/immutability
finding (agents page) was fixed; the 23set-state-in-effectfindings are
disabled pending a dedicated refactor of the prop-to-state sync idiom. - Next.js upgraded from 15.5 to 16.3.
mcp constraint widened to <3 (#184)
mcp>=1.0,<2broadened tomcp>=1.0,<3inpyproject.toml;uv.lock
regenerated souv lock --checkpasses. The locked mcp version is unchanged.
Next 16 regressions fixed: lint gate and themeColor (#256, #257)
next lintwas removed in Next 16;next buildno longer ran ESLint. Added
an explicitnpm run lintCI step and alintscript topackage.json.themeColormoved from themetadataexport toviewportas required by
Next 16;<meta name="theme-color">is now present in the static output again.
The API surface is versioned and frozen as a contract (#193)
- The same handlers are now served under
/api/v1/..., and the versioned
paths are the published contract: they are whatopenapi.jsonfreezes and
what CI checks for drift. The bare/api/...paths keep working as a
compatibility alias but are hidden from the schema, so a client can at last
tell a compatible edit from a breaking one./api/healthstays unversioned
by design.
The backend has a type gate again (#195)
mypynow runs as a CI job over the tier of modules that is annotation-clean
today ([tool.mypy].filesinpyproject.toml). The list is a ratchet: widen
it as modules are fixed. This restores#147's deferred type-check item,
which#176left without a follow-up.
The generated API docs follow the token gate (#144)
/docs,/redocand/openapi.jsonwere served without a token even when
LEVH_TOKENwas set, because the gate only covered the/api/prefix — an
authenticated deployment handed any anonymous caller a complete map of all
108 routes. A browser cannot attachX-LEVH-Tokento the/docsdocument
request, so the surface is now withheld (404) while a token is in force, and
restored deliberately withLEVH_ENABLE_API_DOCS=trueon a trusted network.
The process exposes Prometheus metrics (#145)
GET /api/metrics(and/api/v1/metrics) publishes the in-process registry
in text exposition format: recall/store latency histograms, the embedder
fallback and derived-rebuild counters, and the admission-verdict
distribution. The DockerHEALTHCHECKscrapes it as well, so a process whose
metrics surface is down no longer reads as healthy.- Every request gets a
request_id— reused fromX-Request-IDwhen it is a
sanealnum/-_.:string under 128 chars, generated otherwise, echoed in the
response header — and it is injected into each log record, which is what lets
the recall → admission → store lines be stitched back together. With
LEVH_LOG_JSON=1the logs are emitted one JSON object per line instead of the
human-readable format. GET /api/readyzseparates readiness from liveness: it pings SQLite, reports
the live embedder mode and whether derived state is behind, and answers 503
with the reasons when it is not ready.
The supply chain is pinned and audited (#146)
uv.lockpins the full backend graph and CI installs from it with
uv sync --frozen, so one commit no longer resolves to a different set of
packages weeks later;uv lock --checkfails the build on a stale lock. The
audit follows the lock too:pip-auditruns againstuv exportoutput rather
than whatever pip happened to resolve that day, and Dependabot keeps both the
lock and the npm graph current.- Releases now carry CycloneDX SBOMs for the Python and frontend artifacts, and
asastjob runs Bandit at Medium+ so a new finding cannot land under the
gate unnoticed.
Hook installers no longer promise a checkpoint they cannot install (#124)
install_claude_code_hookandinstall_universal_hookaccepted a
with_checkpointargument and silently dropped it: nothing read the
value, the CLI never defined--with-checkpointso the path was
unreachable, and_CHECKPOINT_TEMPLATEhad no callers. The dead
parameter and the unused template are gone, and the module docstring no
longer advertises a periodic-checkpoint capability these hooks do not
provide. Recurring checkpoints remain available vialevh checkpoint auto.
A row the model cannot read no longer takes the store down (#267, #273)
- A store row the model rejects — an outside agent writing
memory_type="long_term", or a NULLidthat SQLite allows on a non-INTEGER
primary key — used to abort every startup:EpisodicMemory.get_all()raised
out ofMemoryEngine.initialize()and every other memory became unreachable.
Such rows are now quarantined: skipped with a warning that names the row, so
the store stays readable. - The store now enforces the model's own contract on INSERT and UPDATE through
triggers generated from one rules table in the schema module (id, content,
memory_type, importance, frequency, timestamps). A rejected write names the
column and what the model needs, and the store file is left untouched. - A failed startup no longer leaves the process alive forever: the engine is
published beforeinitialize()and shut down in every outcome, so
levh serveexits non-zero on startup failure instead of hanging on the
open aiosqlite worker thread.
Quarantined rows are visible, and doctor counts them the way the read path does (#270, #272)
- Every quarantine increments
levh_memory_rows_quarantined_totalin the
Prometheus registry, andlevh doctorfails on any row the current model
cannot accept — so a store quietly rotting under an external writer produces
a standing signal instead of passing a health review silently. - The doctor count now judges rows by meaning rather than storage shape:
metadataandtagsare NULL when never set, and reading those columns raw
made every sparse row look invalid (2 genuinely unreachable rows read as 18).
The storage-shape-to-model conversion lives in one place —
server.core.db.memories.row_to_memory_dict— shared by the query layer and
the doctor check, with an equivalence test pinning the two together. levh doctorinspects SQLite from a running event loop, so the check no
longer trips on the async driver it is reading through (#271).
Structured startup and static-export polish (#284, #268)
levh serveprinted its startup banner with bareprint(), so
LEVH_LOG_JSON=1did not make it structured even thoughlogging.py
advertised that path. It now routes throughemit()— verbatim when JSON is
off, a JSON record when on — and the stale docstrings describe whatemit()
is actually for (#284).- The static-export logo opts out of
no-img-elementexplicitly, so the
frontend lint gate stays green without a blanket rule change (#268).
Dependency and CI maintenance
- Dependabot bumps across the frontend graph (vitest 2 → 5 with a vite 6.4
floor, jsdom 25 → 30, recharts 2 → 3,@types/node20 → 26, lucide-react,
@testing-library/jest-dom) and the workflow graph (actions/setup-node,
actions/setup-python,actions/upload-pages-artifact,
astral-sh/setup-uv), plus dependabot rules that stop it opening major
TypeScript and ESLint bumps that would need a migration of their own.
v2.31.0
Now installable, offline-first (PWA) (#86)
- LEVH can be installed as a standalone app:
manifest.webmanifest(name,
theme color, maskable 192/512 icons), an offline-first service worker
(sw.js, static assets cache-first, shell network-first with an offline
fallback, API/MCP passthrough) and deferred registration
(pwa-register.js).
Findings inbox — LEVH reports what it noticed, a human decides (#83)
- A new inbox where internally-noticed items surface instead of disappearing.
The dashboard lists them under Findings; each can be accepted
(/api/findings/{id}/decide) or left. Nothing is auto-applied or deleted.
A safer, quieter librarian (#84)
- Removed the librarian's shell authority and resolved the LLM endpoint once
at startup instead of per unit of work. - Provider quota refusals are now explained (model/supplier 429) instead of
hidden, and active agents are no longer mislabelled as silent. - Watchdog/event-loop robustness fixes.
Continuity briefs for every agent (#81)
- Auto-brief and non-repeating auto-summary for all agents; a
get_continuity_briefMCP tool with auto-inject, and the brief now leads
with the last checkpoint.
The embedder tells you when it degrades
GET /api/confignow reportsrequested_embedder_modealongside the
effectiveembedder_mode, plusembedder_fallback_reasonwhen an
auto→ hash fallback happens, so degraded scoring is never silent.
Build & reliability
- Pinned
mcp<2(the 2.x SDK renamedFastMCPtoMCPServer), fixing CI. - Anchored the frontend workspace root (
outputFileTracingRoot) so a stray
package-lock.jsonin a user's home can no longer break local@/*
alias resolution. - Docs/README hygiene: removed roadmap, demo assets, and stale references.
v2.30.0 - Universal Agent Tracking + Frontend Redesign
What's New
Universal Agent Tracking System
- Track any AI agent with auto-heartbeats
- Agent Activity dashboard with real-time status
- Auto-connect hooks with continuity brief
- Agent collaboration tools
Frontend UX Redesign
- Memory Quick Add FAB with templates
- Improved Memories page with tag cloud
- Visual Connectors with category groups
- Client selector cards
Bug Fixes
- Admission gate holds 'review' candidates
- Backup carries attachment bytes
- Session deletion API
- Windows encoding fixes
See CHANGELOG.md for full details.
LEVH 2.27.2 - human and agent memory positioning
Human and agent memory positioning
- Updated package, API, CLI, README, landing-page, and launch metadata to describe LEVH as a local-first memory layer for AI agents and humans.
- Added
levh --versionfor standard CLI release identification.
LEVH 2.27.1 — AGPL metadata alignment
License metadata alignmentnnThis patch release aligns the PyPI package metadata with the repository license: GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later).nnNo runtime behavior changes.nnValidation: release.py --check passed; full suite: 523 passed with 3 known Windows environment-specific tests excluded; wheel and sdist passed twine check.
LEVH 2.27.0 — full rename to LEVH
Full rename to LEVHnn- levh is now the primary CLI command; stackmemory remains as a deprecated compatibility alias with a warning.n- Added canonical LEVH_environment variables withSTACKMEMORY_ fallback support.n- Updated generated MCP configs, dashboard text, README, documentation, and tests to use LEVH-facing names.n- Preserved Python module paths, .stackmemory/, and stackmemory.db for compatibility.n`nValidation: release check PASS; targeted suite 65 passed; full suite 523 passed with 3 known Windows ENV-ISSUE tests excluded; wheel and doctor checks PASS.
LEVH 2.26.8 — first public release
2.26.8 — Release consistency and operational hardening
- Enforced local-first automatic embedding selection; cloud embeddings now require explicit opt-in.
- Added SQLite durability, migration, FTS, timeout, rate-limit, backup, and connector timeout hardening.
- Added release version consistency checks across the server, frontend, and packaged dashboard.
Full test status: 523/523 in the independent Linux reference environment. Windows has three known environment-specific issues, independent of this release.