Changelog¶
All notable changes to Lithos are documented here. The full changelog is maintained in the main repository.
Unreleased (on main)¶
Shipped on main after the v0.4.0 tag; running in source/main-built deployments today.
Added¶
- Short ID prefixes accepted everywhere (PR #412): every task/note id parameter takes an unambiguous git-style prefix (≥ 6 chars). Ambiguity fails loudly with the new
ambiguous_id_prefixcode carrying up to 5{id, title}candidates; mutating responses now echo the resolved full id + title. Behaviour tightened along the way: unknown 6–35-char task prefixes returntask_not_foundon every task tool (previouslyclaim_failed/claim_not_found/silently empty), andlithos_writewith an unknownidreturns anote_not_foundenvelope instead of a protocol-level error. See Envelopes, Errors & IDs. updated_aton task records (PR #416): last-modified stamp bumped by every task-row write (create/update/complete/cancel/reopen — claims never bump it), returned by all task-fetching tools, carried in row-mutating events, and echoed by mutating responses. Compare stamps for equality to detect concurrent edits. Existing databases are migrated in place.lithos_retrievedegradation signal (PR #397): responses always carrydegradedandfailed_scouts, so callers can distinguish partial results (a backend down) from an empty corpus. New alertable metriclithos.lcma.scout.failures.- Salience recalibration (PR #402): decay now bottoms out at
lcma.salience_floor(default 0.3) instead of zero; new non-decayingusage_scoreresult field feeds reranking; newlithos recalibrate-salienceCLI command backfills collapsed databases; decay/reinforcement constants became config fields. - Local/remote LLM synthesis (PRs #405, #406): new
lcma.llmconfig block (any OpenAI-compatible endpoint — Ollama, llama.cpp, vLLM, hosted). The background enrichment worker has the LLM adjudicate typed edges between semantically-close notes, written toedges.dbasprovenance_type: "inferred"with rationale and confidence as evidence. Enabled only whenbase_urlis set — unset is a strict no-op. Token spend bounded by a daily budget. - SSE
resynccontrol event (PR #400):GET /eventsnow tells reconnecting clients when theirLast-Event-IDfell off the replay ring buffer, instead of silently under-delivering. Consumers must handleevent: resync.
Fixed¶
- Bare YAML dates in frontmatter broke indexing (PR #411): an unquoted
created: 2026-07-30could silently drop notes from search — and after an internal refactor, crash the startup rebuild. Both YAML ingestion points now normalize date objects; hand-edited frontmatter can no longer abort the startup scan. - Docker:
LITHOS_LCMA__LLM__*env vars are now forwarded into the container (PRs #409, #410), andllm.max_output_tokensdefault raised 1024 → 4096 for reasoning models.
v0.4.0¶
Released: 2026-07-06 · GitHub Release · PyPI · Docker Hub
Install:
Breaking — canonical error envelope (PRs #370, #371)¶
Every tool failure now returns exactly {"status": "error", "code": "<stable_code>", "message": "..."}:
- Validation failures use
code: "invalid_input"— including paths that previously raised protocol-level errors (unparseable datetime filters onlithos_list,lithos_agent_list,lithos_finding_list). - Error envelopes no longer include a
warningskey. - Deliberately unchanged: actionable write outcomes keep their own top-level
status(version_conflictwithcurrent_version,duplicate,slug_collision,path_collision), all success shapes, and the{"success": ...}claim envelopes. - Migration: replace
status == "invalid_input"branches withstatus == "error" and code == "invalid_input"(except onlithos_write/lithos_note_update, where the code remains the status). This partially reverses 0.3.0's error-status promotion. Full guide: Envelopes, Errors & IDs.
Added — task graph (Phases 1–3)¶
Dependencies became first-class edges (task_edges table + task_type column), adding six tools — lithos_task_edge_upsert, lithos_task_edge_list, lithos_task_ready, lithos_task_blocked, lithos_task_children, lithos_task_spawn (PRs #342, #343, #356):
- Edge types
blocks,parent_child,discovered_from,waits_on_gate; cycle-checked on write. lithos_task_creategainstask_type(task/epic/gate),depends_on,parent_task_id;lithos_task_completereturnsunblocked.- Gates model external waits (
human/timer/ci/pr/external_task) — Lithos never polls; timer gates auto-resolve at query time. - A cancelled predecessor leaves dependents
blocker_unsatisfiable, never spuriously ready. - Breaking:
metadata.depends_on/blocked_onare rejected withinvalid_metadata_key; a one-time migration backfilled existing conventions intoblocksedges.
Added — lifecycle & notes¶
lithos_task_reopen(PR #357): terminal tasks return toopen, with areblockedlist, a durable[Reopened]finding, and atask.reopenedevent.lithos_task_updatenow works on terminal tasks (#303).lithos_note_update(PR #363): patch a note's frontmatter (tags/metadata/title/status) without resending the body.
With these, the tool count reached 37.
Changed¶
- Python 3.12 is now required (PR #365).
v0.3.3¶
Released: 2026-06-10 · GitHub Release
Packaging fix (PR #338): the published wheel no longer carries the spaCy model as a direct-URL dependency (which broke pip install lithos-mcp). The en_core_web_sm model is downloaded on first use instead.
v0.3.2¶
Released: 2026-06-09 · GitHub Release
Breaking¶
- Transport
sserenamed tohttp(PR #304) — no back-compat alias.lithos serve --transport httpnow serves bothPOST /mcp(StreamableHTTP, stateless) andGET /sse(legacy) on one port; any compliant MCP client connects without a bridge. Update CLI invocations, systemd units, and compose overrides.
Added¶
- Entity extraction (PRs #313, #321, #329): wiki-links + spaCy NER + corroborated heuristics populate an
entitiesfrontmatter list, with extractor provenance so agent-curated entities are never clobbered. New CLI commandlithos extract-entities. entitiesfilter onlithos_searchandlithos_list(PRs #316, #319): exact-match AND filtering, inverted-index backed; entities also boost full-text ranking.
Fixed¶
lithos_cache_lookupno longer crashes when a candidate hasconfidence: null; confidence is healed on read and validated on write (PR #314).- The Docker image bakes in the embedding model and loads it offline — no more HuggingFace rate-limit failures at container start.
v0.3.1¶
Released: 2026-06-05 · GitHub Release · PyPI
First PyPI release: pip install lithos-mcp.
Added¶
- Free-form
metadataon knowledge notes (PR #305): arbitrary key/values persisted to frontmatter vialithos_write(metadata=...), returned bylithos_read/lithos_list. Update semantics: omit preserves,{}clears, non-empty merges per key withnulldeleting a key. metadata_matchfilter onlithos_listandlithos_task_list(PR #306): AND across keys; matches scalar equality or list containment; index-backed on notes, SQL-pushed on tasks.
v0.3.0¶
Released: 2026-05-27 · GitHub Release
Breaking¶
tasks.completed_atrenamed toresolved_at(PR #288) — now written on both terminal transitions (complete and cancel), with an in-place schema migration.lithos_task_listgains aresolved_sincefilter; thetask.completedevent field renamed to match.lithos_task_update(metadata=...)became an additive per-key merge (PR #291):{"key": null}deletes a key, unmentioned keys are preserved,{}is a no-op; there is no wholesale clear. Runs in a single transaction so concurrent writers on different keys can't clobber each other.- Write error codes promoted to top-level
status(PR #217):lithos_writefailures becamestatus="slug_collision"etc. (Partially reversed for other tools by 0.4.0's canonical envelope — see above.)
Added¶
lithos_task_get(PR #294): single-task fetch with the full record and an explicittask_not_foundenvelope;lithos_task_statuswidened to the same field set.lithos_task_list(with_claims=true)(PR #221): inline active claims per task in one batched query.metadataon tasks (PR #216) — arbitrary JSON at create time.
Fixed¶
lithos_write(path="a/b.md")treats a.md-terminated path as the complete filename instead of silently creating a directory namedb.md(PR #301).- File-watcher events carry the canonical
{id, title, path}payload, so external Obsidian edits are no longer dropped by id-filtering consumers (PR #298); external renames preserve the document id. task.updatedevents are actually emitted (PR #284).
v0.2.1¶
Released: 2026-04-18 · GitHub Release · PyPI · Docker Hub
Install:
Added¶
-
lithos_related— composite graph tool (PR #188): New MCP tool that merges wiki-links, provenance chains, and LCMA typed edges into a single response. Eliminates the need to fan out across multiple tools and manually join results.Response shape:
{ "id": "<doc>", "included": ["links", "provenance", "edges"], "links": { "outgoing": [...], "incoming": [...] }, "provenance": { "sources": [...], "derived": [...], "unresolved_sources": [...] }, "edges": { "outgoing": [...], "incoming": [...] }, "related_ids": ["<uuid>", ...] }includecontrols which backends are queried — omit a key and it's excluded from the response entirely.related_idsis the deduplicated union of all referenced document IDs (excluding the queried doc itself).depthis supported (1–3) for the links and provenance sections.namespacescopes theedgessection only.
→ See
lithos_related -
--telemetry-consoleflag onlithos serve(PR #187): DX shortcut that enables in-process OTEL console exporters without a collector. Metrics and spans go to stdout — useful for local debugging. -
LCMA MVP2 (PR #170, #177, #176): Second phase of the Layered Cognitive Memory Architecture. Adds OTEL metrics coverage across the full LCMA pipeline and comprehensive structured logging throughout the LCMA codebase.
-
Multiple environment support (PR #180): Lithos now supports separate named environments (e.g.
prod,staging) with independent OTEL tracking. Useful for running multiple Lithos instances on the same host. -
outcomeparameter onlithos_task_complete(fix for PR #182): Tasks can now record a completion outcome string. The value is persisted on the task row and included in thetask.completedevent payload for LCMA consolidation.→ See Task Tools Reference
Removed¶
-
lithos_linksandlithos_provenanceMCP tools (PR #190): Both tools are fully superseded bylithos_related. Their behaviour is available via:lithos_related(id=..., include=["links"])— replaceslithos_linkslithos_related(id=..., include=["provenance"])— replaceslithos_provenance
lithos_edge_listis retained as it supports global edge queries that don't require a single document centre.Breaking change
Remove any calls to
lithos_linksorlithos_provenanceand replace withlithos_related. Theinclude_unresolvedparameter fromlithos_provenanceis dropped — unresolved sources always surface on the composite path.
Fixed¶
lithos_task_completeacceptsoutcomeparam (PR #182): FixesUnexpected keyword argumenterrors when callers passedoutcome=tolithos_task_complete.- Rerank weights rebalanced (PR #183):
vector=0.21, lexical=0.22(was0.25/0.18). Exact-term lexical hits can no longer be structurally dominated by vector-only candidates at equal normalised score. - OTEL gauge caches primed at startup (PR #184): Eliminates stale/missing gauge readings at server start.
setup_logginghardened + handler eviction between tests (PR #185): Prevents log handler accumulation and test-isolation issues.lithos_searchtags filter with colons (PR #192): Tags containing colons (e.g.type:pattern) were silently dropped by the FTS filter. Fixed.lithos_listcontent_query pushes tags/author/path_prefix into FTS (PR #211): Filter fields were not being applied whencontent_querywas used.- LCMA: snippet against
full_content(PR #210): Snippet generation now runs against the full document content, so title-matching passages surface correctly. - LCMA: stored salience emitted in
lithos_retrieveresults (PR #209): Previously salience was not forwarded in result payloads. - LCMA: tag filter uses AND semantics across scouts (PR #208): Tags filter is now applied consistently with AND semantics across all LCMA scouts.
- Server: search mutations wrapped in
asyncio.to_thread(PR #213): Prevents blocking the event loop during write-side search operations. - uv commands corrected in README (PR #212): Dev setup commands updated.
Refactored¶
KnowledgeManager.get_cached_metapublic accessor (PR #186): Exposes the metadata cache via a clean public API for callers that embed lithos as a library.- Build: project now uses
uvexclusively, with Makefile (PR #165):uv sync --extra devfor dev installs;make check,make test, etc. for common actions.
v0.2.0¶
Released: 2026-04-12 · GitHub Release
Added — LCMA MVP1 (Layered Cognitive Memory Architecture)¶
lithos_retrieve— cognitive retrieval tool that orchestrates parallel scouts (vector, lexical, provenance, task-context) with merge-and-normalize, Terrace 1 reranking, and audit receipt logging. Returnsreasons,scouts,salience,temperature,terrace_reached, andreceipt_idper result. Seelithos_retrieve.lithos_edge_upsert— create or update typed edges inedges.db. Upsert key is(from_id, to_id, type, namespace).lithos_edge_list— query edges fromedges.dbby optional filters (from_id,to_id,type,namespace).lithos_writeLCMA fields:note_type,namespace,access_scope,summaries,schema_version— all optional and additive; existing documents are unaffected.- Receipts logging: every
lithos_retrievecall writes an audit receipt (rcpt_*) for full observability.
v0.1.8¶
v0.1.7 skipped
v0.1.7 was skipped due to a tag issue. This release covers all changes between v0.1.6 and v0.1.8.
Added¶
-
lithos_searchnow supportsmode="graph"(PR #146): Traverse the knowledge graph from a starting document and return linked results. Returns documents reachable by wiki-link relationships rather than text/vector similarity. Seelithos_searchfor details. -
lithos_statsextended with health indicators (PR #159): The stats response now includes ahealthblock with pass/warn/fail indicators for each subsystem (index, embedding, coordination). Surfaces the same signal asGET /healthbut in machine-readable per-subsystem form. -
Observability — OTEL tracing (PRs #148, #151): OpenTelemetry tracing added to all untraced code paths. An OTEL log bridge enables trace-log correlation — structured log entries now carry
trace_idandspan_idfields. -
Observability — metrics (PRs #87, #89, #96, #97, #99, #101, #149, #150, #155, #156, #157): Prometheus-compatible metrics exposed across the server:
lithos.knowledge.write_duration_mshistogram — write latency distribution- Resource gauge metrics (documents, chunks, agents, open tasks)
- Startup duration and file watcher event counters
- Event bus subscriber drop and buffer utilisation gauges
- Per-tool call counters and per-tool error counters
- SSE active clients gauge and events-delivered counter
-
Always-on structured JSON logging (PRs #140, #152, #153): All log output is now structured JSON (when
LITHOS_LOG_LEVELis set to any level). DEBUG traces added for link resolution and slug computation. Coordination and knowledge modules emit structured events for observability pipelines (Loki, CloudWatch, etc.). -
Read audit logging (PR #147):
lithos_readcalls are now written to an audit log (append-onlyread_audit.jsonl). Each entry recordstimestamp,document_id,agent, andpath. Useful for compliance and debugging access patterns.
Fixed¶
-
Preserve incoming edges on document update (PR #139): Previously, updating a document that other documents linked to would silently drop those incoming wiki-link edges from the graph. They are now preserved correctly.
-
Docker healthcheck uses HTTP
/health(PR #143, closes #72, #77): The defaultdocker-compose.ymlhealthcheck now callsGET /healthinstead of using a TCP probe. This gives accurate health signals to Compose, Swarm, and Kubernetes. -
Recover from corrupt ChromaDB stores (PR #160): If the embedding store is detected as corrupt on startup, Lithos now logs a clear error and attempts an automatic rebuild rather than crashing. Contributed by @peterbrown05 (first contribution 🎉).
-
ChromaDB metadata types (PR #145, closes #42): Fixed a type error where ChromaDB's
metadataslist was incorrectly typed, causing failures on some document writes.
Refactored¶
-
KnowledgeManagernow requires explicit config (PR #158, closes #35):KnowledgeManagerno longer accepts implicit defaults. All callers must pass aLithosConfiginstance. This only affects users embeddinglithosas a Python library — the CLI and MCP server are unaffected. -
embed_async()dead code removed (PR #144, closes #74): The unused async embedding path was removed to simplify the codebase.
v0.1.6¶
Fixed¶
- PyPI publish fix. No functional changes. Re-publish to resolve a packaging issue with the v0.1.5 release.
v0.1.5¶
Breaking Changes¶
-
lithos_healthMCP tool replaced by HTTP endpoint.GET /healthis now a plain HTTP endpoint (returns200 OKwhen healthy,503when degraded). It is no longer an MCP tool. Update any callers usinglithos_health()to usecurl http://<host>:8765/healthor an HTTP client instead. -
lithos_semanticMCP tool removed. Uselithos_searchwithmode="semantic"for pure semantic search, or the new defaultmode="hybrid"for best results. -
lithos_searchnow defaults to hybrid mode. Existing callers that relied onlithos_searchfor full-text-only results will now receive hybrid (BM25 + semantic RRF) results. Passmode="fulltext"explicitly to restore the previous behaviour. -
similaritykey renamed toscorein search results. Callers migrating fromlithos_semanticthat readresult["similarity"]must update toresult["score"]. All three modes (hybrid,fulltext,semantic) now use a unifiedscorefield. -
agentis now required onlithos_delete. Previously optional. Callers that omit it will receive aTypeErrorfrom the MCP layer. -
sort_by_confidenceremoved fromlithos_cache_lookup. Results are now always sorted by confidence score. Remove thesort_by_confidenceparameter from any calls that use it.
Added¶
-
lithos_task_list— list tasks with optional filters:agent,status("open"|"completed"|"cancelled"),tags(AND), andsince(ISO timestamp). -
lithos_task_cancel— cancel a task, releasing all active claims. Takestask_id,agent, and an optionalreason. -
lithos_task_update— update mutable task metadata (title,description,tags) without closing the task. At least one field must be provided. -
lithos_searchnow accepts amodeparameter:fulltext|semantic|hybrid(default:hybrid). - Hybrid search mode merges Tantivy (BM25) and ChromaDB (cosine similarity) results using Reciprocal Rank Fusion (RRF, k=60) for improved ranking quality.
- Unknown
modevalues now return a structured{ "status": "error", "code": "invalid_mode", ... }dict instead of raising aValueError. lithos_tagsaccepts an optionalprefixparameter to filter tags by prefix.lithos_listaccepts two new optional filters:title_contains(substring match on title) andcontent_query(full-text search within results).
Fixed¶
-
lithos_readreturns structured error on missing document (issue #102): Previously propagated a rawFileNotFoundError. Now returns{ "status": "error", "code": "doc_not_found", "message": "..." }. -
Consistent error envelopes across all tools (issue #85): Coordination tools (
lithos_task_claim,lithos_task_renew,lithos_task_release,lithos_task_complete) andlithos_deletenow all return the standard{ "status": "error", "code": "...", "message": "..." }envelope on failure paths.Tool Error code lithos_delete(not found)doc_not_foundlithos_task_claim(conflict)claim_failedlithos_task_renew(no claim)claim_not_foundlithos_task_release(no claim)claim_not_foundlithos_task_complete(missing/closed)task_not_foundlithos_task_cancel(missing/closed)task_not_foundlithos_task_update(missing)task_not_found
Schema Changes¶
-
versionfield in frontmatter (issue #45, PR #55): All knowledge documents now have aversion: 1integer field in their YAML frontmatter for optimistic locking. Existing documents without this field are treated asversion: 1on first read — no migration needed.The
lithos_writetool now accepts an optionalexpected_versionparameter. If provided and the document's current version doesn't match, the call returns aversion_conflicterror.
Migration Guide¶
From lithos_semantic to lithos_search¶
Before:
results = lithos_semantic(query="how to run async tasks in python")
# results[0]["similarity"] ← old key
After:
results = lithos_search(query="how to run async tasks in python", mode="semantic")
# or use the new default hybrid mode:
results = lithos_search(query="how to run async tasks in python")
# results["results"][0]["score"] ← new key
Fixing lithos_delete calls¶
Before:
After:
Fixing coordination error handling¶
Before:
result = lithos_task_claim(task_id="...", aspect="...", agent="...")
if result.get("success") == False: # old pattern
print("Claim failed")
After: