Skip to content

Releases: VonderVuflya/Yggdrasil

v0.11.0

Choose a tag to compare

@VonderVuflya VonderVuflya released this 15 Jul 20:05

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 (was demo-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.*.bak backup is written first, and the
    resolved identity is pinned explicitly into config.json so no future
    default change can ever strand memory again
    . FTS is untouched (it indexes
    content, not identity).
  • ygg sync users: 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 hardcoded demo-user fallbacks were routed through it, so
    the default can no longer diverge across the codebase. The demo/eval gates pin
    the demo identity explicitly via ygg_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/embed that some builds and
    hosted proxies serve instead); it sent no User-Agent, so the Cloudflare proxy
    in front of *.proxy.runpod.net 403'd every request (now identifies as a
    product string, like ygg seed already 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.py now 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_TOKEN env lookup in four
    gates; drop stale "MVP" wording and engine.sengine's typos (roadmap #14/#17).

v0.10.0

Choose a tag to compare

@VonderVuflya VonderVuflya released this 15 Jul 00:10

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; --json for scripting. Closes docs/TODO §6.

Added

  • Hardware-aware acceleration tier + GPU warning (ygg recommend / hw).
    hw() now classifies inference as cpu / metal / cuda / rocm/vulkan and,
    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
    upgrades qwen2.5:3b, qwen3:4b-instruct-2507, and gemma3:4b. If the local
    store is dominantly Russian/Chinese, recommend prints a steer away from
    English-only models.

Changed

  • The recommended quality upgrade is now qwen2.5:3b, not llama3.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

Choose a tag to compare

@VonderVuflya VonderVuflya released this 04 Jul 20:10

Fixed

  • ygg seed crashed 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

Choose a tag to compare

@VonderVuflya VonderVuflya released this 04 Jul 20:01

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 a relations.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/--contradicts links at write time (also
    exposed on the ygg_remember MCP tool, so an agent saving a fix can close the
    follow-up it resolves); ygg supersede --by records the replacement;
    ygg review dup consolidation now leaves SUPERSEDES edges instead of bare
    archives. Inspect with ygg 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; off removes it, status reports. 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 crashed ygg export-native on the 🌳 in
    the managed block; Codex session routing compared against str(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

Choose a tag to compare

@VonderVuflya VonderVuflya released this 04 Jul 18:49

Seeding hardening — found and fixed on a live multi-hour ygg seed run.

Added

  • Distill on any LAN box or phone. --ollama-url now 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 whole distill_timeout; servers
    that don't stream fall back to a non-streaming call capped at the idle window.
  • ygg seed output 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 … unchanged rows. Non-TTY/agent output is
    unchanged (byte-stable).

v0.7.0

Choose a tag to compare

@VonderVuflya VonderVuflya released this 04 Jul 18:32

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.loads every scoped embedding per query; cosine is a dot of two cached unit
    vectors. Measured A/B vs the previous engine: dense search() 10–29× faster and
    the speedup grows with store size (at 6k memories, 8.1 s/query → 0.28 s). Still
    zero-dependency (stdlib array, no numpy).
  • Lexical search pushes ORDER BY bm25 LIMIT into 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.py subprocess per call,
    with stdout/stderr separated so --json stays parseable.
  • Index on (user_id, namespace, created_at) for the session-start hook's get_all.

Changed

  • Embedding model is versioned per row. Switching YGG_EMBED_MODEL marks 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 --report leads 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 in AGENTS.md/MEMORY.md
    (idempotent; preserves hand-written content). Pairs with ygg 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 --yes auto-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 /add bypassing 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

Choose a tag to compare

@VonderVuflya VonderVuflya released this 04 Jul 16:38

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 --id and ygg reset --project|--source|--type|--all — recover from a
    bad ygg seed without sqlite surgery. reset previews the count and demands typed
    confirmation (or --yes). The engine's /delete + /purge are the only destructive
    endpoints and are deliberately not exposed as MCP tools.
  • GET /get?id= — direct indexed lookup; ygg materialize now works at any store
    size (the old scan couldn't reach memories past the first 1000).
  • distill_num_ctx setting (default 8192) — seed distillation sends options.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 and ygg hooks are enabled (atomic per-session /
    per-prompt locks; registration dedupe by script name).
  • ygg bootstrap used legacy type names absent from the canonical enum — typed
    memories got no ranking boost.
  • Deleted dead code (materialize_memory.py twin, dead engine_token(), duplicated
    gate env lookup); docstrings caught up with the service.py rewrite.
  • 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 (flaky SEED FAILED).

v0.5.5

Choose a tag to compare

@VonderVuflya VonderVuflya released this 04 Jul 10:10

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 used unicode61, so e.g. a
    Russian query matched nothing in lexical mode. Also splits snake_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 doctor no longer prints "All good." with no MCP registration anywhere.

Security

  • No yggdrasil-demo-token fallback in the engine — a bare ygg serve reuses or
    generates the 0600 ~/.yggdrasil/token instead 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_materialize output confined to the vault root — a remote MCP client could
    previously write attacker-seeded .md anywhere 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 .mcpb desktop bundles (hosted on GitHub Releases); ignore
    .cache/, .claude/, scratchpad/.

v0.5.4

Choose a tag to compare

@VonderVuflya VonderVuflya released this 28 Jun 21:48

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

Choose a tag to compare

@VonderVuflya VonderVuflya released this 28 Jun 20:21

🎉 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 meaningful title; 100%
    per-parameter description coverage; and an enum on type (the canonical memory
    categories — also curbs type drift). Estimated per-tool TDQS ~4.3–4.5 (tier A),
    up from C.

Fixed

  • MCP serverInfo reported a stale dev identity (yggdrasil-mvp / hardcoded
    0.1.0) to every host — now reports yggdrasil and the real installed version
    (read from package metadata). Surfaced by Glama showing the server as 0.1.0.

Added

  • Dockerfile — lets Glama and any container host build, start and introspect the
    MCP server (the stdio facade answers initialize/tools/list cold, 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 as NOASSERTION) 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 hooks now installs both retrieval hooks (SessionStart bootstrap + this).