Releases: VonderVuflya/Yggdrasil
Release list
v0.11.0
Retire the demo-heritage identity so real memory no longer lives under a
throwaway "demo" name (roadmap #16), and fix dense search against remote /
proxied Ollama endpoints.
Changed (BREAKING)
- Default identity is now
local/personal(wasdemo-user/
yggdrasil-demo). On the first engine start after upgrade, existing memory is
auto-migrated once — a version-guarded (PRAGMA user_version) SQL relabel
that only touches the exact legacy demo pair, so a custom identity is never
moved. A timestamped*.pre-identity-v1.*.bakbackup is written first, and the
resolved identity is pinned explicitly intoconfig.jsonso no future
default change can ever strand memory again. FTS is untouched (it indexes
content, not identity). - ⚠
ygg syncusers: upgrade every synced machine together. The sync key
format changes with the identity; a lagging demo-keyed peer is auto-adopted to
the default on import, but mixed versions briefly diverge.
Added
ygg migrate [--dry-run]— preview or run the identity migration manually
(backup-first), for when the daemon isn't the one you want to drive it.- Identity now resolves through a single source of truth (
ygg_config.user_id()
/namespace()); ~15 hardcodeddemo-userfallbacks were routed through it, so
the default can no longer diverge across the codebase. The demo/eval gates pin
the demo identity explicitly viaygg_config.DEMO_*constants.
Fixed
- Dense search silently degraded to lexical against remote / proxied Ollama.
Three independent causes, all fixed: the embedder only called the legacy
/api/embeddings(now falls back to the newer/api/embedthat some builds and
hosted proxies serve instead); it sent noUser-Agent, so the Cloudflare proxy
in front of*.proxy.runpod.net403'd every request (now identifies as a
product string, likeygg seedalready did); and a dropped request left a
memory permanently unembedded (now retries transient failures with backoff).
Benchmark
- Grew the retrieval eval corpus from 35 to 232 memories / 110 labelled queries
(docs/TODO #26) for statistical power — the candidate pool per query roughly
doubles and the recall CIs tighten.ygg_eval.pynow seeds+embeds the corpus
once (not 6×), shows a progress heartbeat, prints per-query-class recall, and
warns loudly if a requested embedding model produced no vectors.
Cleanup
- Dedup the copy-pasted
YGG_ENGINE_TOKEN or YGG_ENGINE_TOKENenv lookup in four
gates; drop stale "MVP" wording andengine.s→engine'stypos (roadmap #14/#17).
v0.10.0
Honest hardware, multilingual-safe models, no truncated stubs, memory-quality
report (docs/TODO §1/§3/§5/§6).
Added
ygg quality— a store health report. Type/project distribution, exact
duplicate pairs (content-hash), near-duplicate pairs (cosine ≥ threshold,
default 0.95), cross-project leakage, and likely-truncated records (reuses the
write-path truncation heuristic). Computed server-side (/quality) so
embeddings never leave the engine;--jsonfor scripting. Closes docs/TODO §6.
Added
- Hardware-aware acceleration tier + GPU warning (
ygg recommend/hw).
hw()now classifies inference ascpu/metal/cuda/rocm/vulkanand,
crucially, warns when a GPU is present but won't accelerate inference — the
Intel-Mac + AMD case, where macOS is Metal-only (Apple-Silicon oriented) and
ROCm doesn't exist, so stock inference runs on CPU regardless of the card. The
catalog surfaces the warning up top instead of silently running on CPU. - Language-aware model catalog. Every model now carries a language/thinking
tag (EN/RU/ZH · non-thinking,⚠ NO Russian/Chinese, …). Added the Qwen
upgradesqwen2.5:3b,qwen3:4b-instruct-2507, andgemma3:4b. If the local
store is dominantly Russian/Chinese,recommendprints a steer away from
English-only models.
Changed
- The recommended quality upgrade is now
qwen2.5:3b, notllama3.2:3b—
Llama 3.2 officially supports English + 7 European languages only, silently
degrading non-English memory. Llama stays in the catalog, clearly flagged.
Fixed
- Truncated lessons are dropped, not persisted. A distilled lesson whose text
ends mid-thought (trailing:/ dangling connector / unbalanced bracket or
quote — e.g. a list intro whose items never arrived) is now discarded at the
write path and counted separately, instead of being stored as a stub. Length
is deliberately not a signal (lessons are meant to be short).
v0.9.1
Fixed
ygg seedcrashed at import on Python 3.10/3.11 — the 0.8.0 seed summary
used a multi-line f-string replacement field (PEP 701, python 3.12+ only), so
the module didn't even parse on older interpreters. 0.8.0 and 0.9.0 on PyPI
are affected; upgrade.- The release pipeline now syntax-checks the package on the oldest supported
Python (3.10 via uv) before publishing, so a 3.12-only construct can't ship
again.
v0.9.0
One memory across your machines, and a store that answers "why".
Added
ygg sync --repo <your-git-repo>— cross-machine memory sync through a repo
YOU own (GitHub private, Gitea, even a bare repo on a USB stick). The store
travels as one byte-deterministic JSON file per memory plus arelations.jsonl
(git union-merge driver); no relay, no account, no cloud in the loop. One
command converges both machines: export → commit → pull → import with a
deterministic merge policy (archive-anywhere-holds-everywhere, longer content
wins, confidence max, pinned OR) → re-export → push. Counters and vectors stay
per-machine by design.- Relation graph — memories can SOLVE, SUPERSEDE, or CONTRADICT each other.
Typed, idempotent edges answer why a memory exists and what replaced it:
ygg remember --solves/--supersedes/--contradictslinks at write time (also
exposed on theygg_rememberMCP tool, so an agent saving a fix can close the
follow-up it resolves);ygg supersede --byrecords the replacement;
ygg reviewdup consolidation now leaves SUPERSEDES edges instead of bare
archives. Inspect withygg relations --id(both directions, with previews). ygg seed --schedule HH:MM— nightly auto-distill. Installs a
calendar-fired LaunchAgent that distills what changed since the last run and
exits;offremoves it,statusreports. Incremental state + dedup keep the
nightly run cheap; a dead Ollama just means those files retry the next night.
Fixed
- Python 3.10 crash in
ygg review/ reports /materialize— the code used
datetime.UTC, an alias that only exists on Python 3.11+. 0.8.0 on PyPI is
affected on 3.10; upgrade. - Windows: explicit UTF-8 everywhere. Text IO carrying memory content relied
on the locale encoding (cp1252), which crashedygg export-nativeon the 🌳 in
the managed block; Codex session routing compared againststr(path)and never
matched Windows backslash paths (as_posix()now). - The release pipeline itself: publishing from a detached HEAD (e.g. a checked-out
tag) no longer fails with "not a full refname" — pushes name the target branch.
v0.7.1
Seeding hardening — found and fixed on a live multi-hour ygg seed run.
Added
- Distill on any LAN box or phone.
--ollama-urlnow speaks three dialects
— Ollama/api/generate, Ollama/api/chat, and OpenAI/v1/chat/completions
(auto-probed, working combo cached) — so LM Studio, llama.cpp-server, exo, and
on-device iPhone LLM-server apps all work as distill endpoints, not just Ollama. - iCloud Obsidian vaults are discovered (
~/Library/Mobile Documents/iCloud~md~obsidian/Documents)
— for many users that's THE vault, and it was silently invisible before. The
per-vault note cap is now 500 most-recent (was 50 alphabetical, which dropped an
arbitrary A–G slice of larger vaults).
Fixed
- Distilled lessons keep the source language. A small model (qwen2.5:3b) would
randomly translate Russian logs into Chinese; the target language is now named
explicitly for Cyrillic-dominant logs. Affects every endpoint, not just phones. - A flaky lesson no longer loses a whole file. Malformed model JSON (missing
comma, truncated tail, empty) is salvaged object-by-object and retried once;
only truly-unparseable output errors out (and is retried on the next run). - A dead distill peer is caught in ~90 s, not the full timeout. Streaming with
an idle window (YGG_DISTILL_IDLE, default 90 s) means a phone that locks or a
Wi-Fi drop no longer hangs a request for the wholedistill_timeout; servers
that don't stream fall back to a non-streaming call capped at the idle window. ygg seedoutput is readable. Only projects where something happened are
logged (✓ demo +1 lessons); all-unchanged projects collapse into one summary
line instead of a wall of+0 new … unchangedrows. Non-TTY/agent output is
unchanged (byte-stable).
v0.7.0
Performance, benchmark honesty, and the native-memory bridge.
Performance
- Embeddings stored as packed float32 blobs (~3.9× smaller than JSON text) with an
in-process cache of unit-normalized vectors — dense search/dedup no longer
json.loadsevery scoped embedding per query; cosine is a dot of two cached unit
vectors. Measured A/B vs the previous engine: densesearch()10–29× faster and
the speedup grows with store size (at 6k memories, 8.1 s/query → 0.28 s). Still
zero-dependency (stdlibarray, no numpy). - Lexical search pushes
ORDER BY bm25 LIMITinto SQLite instead of Python-scoring
every term match. - Batched reindex via Ollama
/api/embed(32/req, per-item fallback) and startup
warmup/reindex moved off the bind path (no lazy-spawn port race). - MCP tool calls run in-process instead of a
python ygg.pysubprocess per call,
with stdout/stderr separated so--jsonstays parseable. - Index on
(user_id, namespace, created_at)for the session-start hook'sget_all.
Changed
- Embedding model is versioned per row. Switching
YGG_EMBED_MODELmarks old vectors
stale and reindexes them, instead of comparing vectors across models.ygg doctor
counts model-mismatched rows. - Benchmark reporting is credibility-first.
eval/ygg_eval.py --reportleads with
holdout recall@1 (weights tuned on dev only): 0.93 within a project, 0.80 full-corpus,
recall@3 = 1.00 in both. Discloses candidate pool sizes (min 2 / median 6 / max 35) and
95% bootstrap CIs. README badge → holdout 0.93; BENCHMARKS.md + all 6 translations
rewritten to the honest two-view framing.
Added
ygg export-native --project P— the native-memory bridge: writes a curated,
type-grouped digest of a project's memory into a managed block inAGENTS.md/MEMORY.md
(idempotent; preserves hand-written content). Pairs withygg seed(which imports from
the native memory) so Yggdrasil is the layer above Claude Code's and Codex's own memory,
feeding them both ways.ygg review [--apply]— work the governance queue from the CLI: consolidate exact/
near duplicates (keep the oldest, archive the rest) and surface stale/conflict markers.
Interactive on a TTY;--apply --yesauto-consolidates duplicates and flags stale markers
for manual review. Everything is archived (reversible), never hard-deleted.- Ranking parity — a pinned or frequently-recalled memory retrieved only by vector now
gets its pin/usage boost, via the same channel lexical hits use.
Security
- Engine-side secret guard — a raw
POST /addbypassing the CLI now also refuses obvious
credentials (AWS keys, JWTs, GitHub/GitLab PATs, private keys, connection-string passwords).
High-confidence structured tokens only, so memories that merely mention "password"/"secret"
are unaffected.
Fixed
- Cross-platform
hw()— Linux/proc/meminfo+/proc/cpuinfo(+ nvidia-smi), Windows
PowerShell CIM; the model recommender no longer sizes off 0 GB off-macOS. - First-hour polish — actionable port-conflict hint instead of a traceback; non-interactive
install announces the lexical-only fallback.
v0.6.0
Robustness, DX and CI — the second slice of the audit plan.
Added
- CI (GitHub Actions): unit tests on ubuntu/macos/windows (py3.10 + 3.13),
behavioural gates on ubuntu/macos, and a benchmark job that fails the build if
lexical recall@1 regresses below 0.77 — the badge becomes a receipt. ygg delete --idandygg reset --project|--source|--type|--all— recover from a
badygg seedwithout sqlite surgery.resetpreviews the count and demands typed
confirmation (or--yes). The engine's/delete+/purgeare the only destructive
endpoints and are deliberately not exposed as MCP tools.GET /get?id=— direct indexed lookup;ygg materializenow works at any store
size (the old scan couldn't reach memories past the first 1000).distill_num_ctxsetting (default 8192) — seed distillation sendsoptions.num_ctx
explicitly instead of inheriting Ollama's default (often 4096, which silently truncated
long transcripts); output cut off by the token limit is rejected, not persisted.
Fixed
- Hooks work on Windows (
python3 … || python …launcher) and context is never
injected twice when both the plugin andygg hooksare enabled (atomic per-session /
per-prompt locks; registration dedupe by script name). ygg bootstrapused legacy type names absent from the canonical enum — typed
memories got no ranking boost.- Deleted dead code (
materialize_memory.pytwin, deadengine_token(), duplicated
gate env lookup); docstrings caught up with theservice.pyrewrite. - Gates run on a dedicated port (42169) instead of sharing the daemon's 42069 — the
runner used to kill the user's daemon and race its restart (flakySEED FAILED).
v0.5.5
The audit release — security & correctness fixes from a full technical audit
(docs/IMPROVEMENT-PLAN.md), no behaviour changes for the happy path.
Fixed
- Lexical search now works for non-Latin text (Cyrillic, Greek, CJK …). The
query tokenizer was ASCII-only while the FTS index usedunicode61, so e.g. a
Russian query matched nothing in lexical mode. Also splitssnake_case. - Engine writes are transactional — an exception mid-write no longer leaves an
open transaction that the next request silently commits (which produced memories
invisible to lexical search). Row + FTS commit or roll back together. - Malformed client input returns a JSON 400 instead of a traceback and a dropped
connection (limit="abc",importance:"high", …). - Editing a memory refreshes its
content_hash— a stale hash corrupted dedup
both ways. ygg doctorno longer prints "All good." with no MCP registration anywhere.
Security
- No
yggdrasil-demo-tokenfallback in the engine — a bareygg servereuses or
generates the 0600~/.yggdrasil/tokeninstead of a publicly-known constant. - The Streamable-HTTP MCP facade refuses to start without a token (
YGG_MCP_INSECURE=1
opts into open mode for local testing). ygg_materializeoutput confined to the vault root — a remote MCP client could
previously write attacker-seeded.mdanywhere the user can write.- Auth token no longer written in plaintext into launchd plists, MCP registrations,
or~/.claude.json— everything resolves the 0600 token file at call time. - Timing-safe token comparison (
hmac.compare_digest) + a Host-header check on
loopback binds (blocks DNS-rebinding drive-bys).
Changed
- Untracked the
.mcpbdesktop bundles (hosted on GitHub Releases); ignore
.cache/,.claude/,scratchpad/.
v0.5.4
Changed
- Relicensed from Elastic-2.0 to GNU AGPL-3.0-or-later. Elastic-2.0 is not an
OSI/SPDX-recognized license, so GitHub (and tools that key off its detection, like
Glama) could never identify it — it showed as "Other/NOASSERTION". AGPL-3.0 is
OSI-approved and auto-detected, and its network copyleft keeps the original intent:
Yggdrasil stays free to use, modify, self-host, and redistribute, but anyone who
modifies it or offers it as a hosted/network service must release their source
under the same license.
v0.5.3
🎉 Yggdrasil is now listed on glama.ai!
The MCP server is indexed, builds and passes introspection, and scores tier A on
Glama's Tool Definition Quality Score. Most of this release is the work that got us there.
Changed
- MCP tool definitions tuned to the TDQS rubric (Glama's Tool Definition Quality
Score) — every tool now has: a tight, front-loaded description that states purpose +
scope and names its sibling tools for disambiguation (recall = all projects,
bootstrap = load one project, search = query one project); explicit when/when-not
guidance; MCP annotations (readOnlyHint/destructiveHint/idempotentHint/
openWorldHint) carrying the behavioral profile; a meaningfultitle; 100%
per-parameter description coverage; and an enum ontype(the canonical memory
categories — also curbs type drift). Estimated per-tool TDQS ~4.3–4.5 (tier A),
up from C.
Fixed
- MCP
serverInforeported a stale dev identity (yggdrasil-mvp/ hardcoded
0.1.0) to every host — now reportsyggdrasiland the real installed version
(read from package metadata). Surfaced by Glama showing the server as0.1.0.
Added
- Dockerfile — lets Glama and any container host build, start and introspect the
MCP server (the stdio facade answersinitialize/tools/listcold, no engine
needed), so directory listings that gate on a Glama check pass. glama.json+ full canonical Elastic-2.0 LICENSE text (was a one-line
pointer GitHub read asNOASSERTION) so the license is detected and the server
is installable from directory listings.- Per-prompt auto-recall hook (
UserPromptSubmit) — the fix for "the agent
forgets to use the memory". On every request it runs a cross-project recall and
injects the top genuinely-relevant matches (raw-cosine gate, so off-topic
prompts inject nothing), and asks the agent to cite what it reused inline as
🌳 recalled: …— the visible breadcrumb that makes the tool's work trustworthy.
ygg hooksnow installs both retrieval hooks (SessionStart bootstrap + this).