Conversation
3 files still referenced ~30ms (stale from early benchmarks). Actual measured P50 at 1K-10K memories is ~45ms per benchmarks.md. Updated: architecture.md, mcp.md, index.md
…-to-45ms fix(docs): standardize recall latency to ~45ms
- Add internal/ to .gitignore (prevents future strategy/launch doc leaks) - Add benchmarks/longmemeval/results_*/ to .gitignore (ephemeral data) - Add docs/package-lock.json to .gitignore (VitePress build artifact) - Move blog/comparison-2026.md → docs/comparison-2026.md (consolidate docs) - Remove stale benchmarks/longmemeval/results_diverse/ (raw output, not summary) - Remove docs/package-lock.json from tracking Audit confirms: zero internal/ files in entire git history after filter-repo scrub.
…#1015) * feat(bench): batch import + strategy flag + API embedding auto-config - Replace 53x individual remember calls with single JSONL batch import (10x speedup) - Add --strategy flag to pass vector|hybrid to uteke recall - Auto-configure embedding API from EMBED_API_KEY/EMBED_API_BASE env vars - Add --chunk-sessions flag for session chunking (Tier 2 prep) - Dedup recall results by session_id (first occurrence = highest rank) - Raise subprocess timeout 120s -> 600s for large imports Initial 5Q results: hybrid R@5=1.000 vs vector R@5=0.800 (+20pp) * fix(bench): only mark sessions inserted after import succeeds Address Cora findings: - Move inserted_sids population to after batch import success - Parse import response to verify imported_count > 0 - Log warning if import returns 0 inserted * fix(bench): resolve uteke binary by absolute path to avoid x86_64 PATH clash Background subprocess calls were resolving to /opt/data/.cargo/bin/uteke (x86_64) instead of target/release/uteke (AArch64). Now resolves relative to repo root with shutil.which fallback. * docs(bench): add 50Q hybrid results — R@5 98.0%, R@10 100% Strategy comparison (uteke only): - Vector 500Q: R@5=85.4%, R@10=88.5%, NDCG@5=0.810 - Hybrid 50Q: R@5=98.0%, R@10=100%, NDCG@5=0.960 - Improvement: +12.6pp R@5 Hybrid uses RRF (k=60) fusion of vector + FTS5 search. 1 miss at R@5 (single-session-user), recovered at R@10. run_eval.py changes: - Add --strategy flag (vector|hybrid) - Throttle: taskset -c 0-1 + nice -n 19 - Absolute binary path resolution - Timeout: 900s per question * fix(core): add dedup, retry, and auto_link to import path (#1005) Import pipeline was missing 3 features that remember() has: 1. Dedup check — cosine >= 0.95 skips duplicate entries 2. Retry on embedding failure — 3 retries with backoff 3. Auto-link cosine edges — graph edges for imported memories Changes: - import_export.rs: call check_duplicate() before insert, use retry_embed() instead of single-attempt embed(), call auto_link_cosine() after successful insert - operations.rs: make retry_embed() and check_duplicate() pub(crate) so they're accessible from import_export.rs Import path is now semantically consistent with remember() path. * fix(core): cross-compile ORT_LIB_NAME for Android/iOS (#1014) Add target_os = "android" to Linux .so branch and target_os = "ios" to macOS .dylib branch. Without these, uteke-core fails to compile for mobile targets with E0425 (cannot find value ORT_LIB_NAME). Android uses libonnxruntime.so (same as Linux, loaded via jniLibs). iOS uses libonnxruntime.dylib (same as macOS, framework embedded). This unblocks uteke-mobile cross-compilation. * feat(core): hybrid as default recall strategy Change default_strategy from vector to hybrid (RRF: vector + FTS5). Benchmark justification (LongMemEval-S): - Vector 500Q: R@5=85.4%, R@10=88.5% - Hybrid 50Q: R@5=98.0%, R@10=100.0% - Improvement: +12.6pp R@5 Changes: - config.rs: default_strategy = hybrid + assert in test - cli.rs: update help text default to hybrid - recall.rs: update comment to reflect new default - configuration.md: update all references - cli-reference.md: reorder examples, hybrid first as default Users who want vector-only: set default_strategy = vector in config or use --strategy vector flag. * fix(bench): guard taskset/nice with sys.platform check Cora finding: taskset is Linux-only, would FileNotFoundError on macOS. Now conditionally applied only when sys.platform == 'linux'. --------- Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…ng text (#1006, #1002) (#1016) #1006: CLI update check skipped when UTEKE_NO_UPDATE_CHECK env var is set. Benchmark script sets this automatically, saving ~500ms per subprocess call. #1002: Pre-truncate text before tokenizer to avoid wasted CPU on tokens beyond MAX_SEQ_LEN (2048). Uses 4 chars/token heuristic with char-boundary safety. #1003: Already resolved — idx_memories_namespace index exists in SCHEMA_INDEXES. #1004: Already resolved — compute_graph_signals uses single batched SQL query. Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…enance (#1007, #1010, #1012, #1013) (#1017) * feat: Phase 3 — lifecycle/deprecated endpoint, memory tools guide, source provenance (#1007, #1010, #1012, #1013) #1007: GET /lifecycle/deprecated — list deprecated memories with sunset info - list_deprecated() in aging.rs with DeprecatedMemoryInfo struct - Handler + route + API registry entry #1010: Memory tools guide injection - guide.rs module with default_guide() for system prompt injection - CLI: uteke guide command - API: GET /guide endpoint #1012: Implicit memory hierarchy docs - docs/organizing-memories.md — type + importance pattern - VitePress nav entry #1013: Auto-populate source provenance - CLI extraction: set source_label from input filename, source_type='extract' - Server extraction: set_source on each extracted fact - CLI output: display source_type alongside source in verbose mode * docs: regenerate api-reference.md for new endpoints (#1007, #1010) --------- Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
SYSTEM_PROMPT now requests scene-segmented JSON output with type and priority per fact. parse_facts handles three formats: scene-segmented nested JSON, flat object array with type/priority, and legacy flat string array (backward compatible). ExtractedFact struct carries content, scene, fact_type, priority. CLI and server callers use remember_typed + set_importance + scene tag. Offline mode unaffected — ExtractedFact::flat wraps existing strings. 9 new tests cover nested parsing, backward compat, dedup, priority range validation. Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
- Version bump to 0.14.0 - CHANGELOG entry for v0.14.0 - Docs: cli-reference scene-segmented extraction section - Docs: memory-lifecycle deprecated endpoint Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
- Add 60s/30s/30s delay between crate publishes for index propagation - Add repository, rust-version, documentation, homepage to uteke-mcp - Fixes: uteke-mcp/cli/server fail because uteke-core not yet in index - Fixes: 'manifest has no documentation, homepage or repository' warning Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
Root cause: commit 0b5cad3 removed docs/package-lock.json from git tracking and added it to .gitignore. The deploy-website workflow still referenced it via cache-dependency-path, causing setup-node to fail: 'Some specified paths were not resolved, unable to cache dependencies.' Fix: - Replace actions/setup-node with oven-sh/setup-bun - Replace npm ci with bun install --frozen-lockfile - Replace npm run build with bun run build - Add docs/bun.lock as tracked lockfile - Remove docs/package-lock.json from .gitignore (no longer relevant) Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
Add mutation testing infrastructure to validate test quality across critical pure-logic modules. 11 new mutation-killing tests improve salience_recency.rs score from 76% to 96%. Changes: - Add cargo-mutants config (mutants.toml) with exclusions for modules requiring external services - Add mutation-testing CI workflow (develop→main PRs only, pre-release quality gate) - Add .gitignore entries for mutants output directories - Add mutation-testing.md developer documentation - Write 9 mutation-killing tests for salience_recency.rs - Write 2 mutation-killing tests for recall_cache.rs Results (cargo-mutants v27.1.0): jaccard.rs: 12 mutants, 9 caught, 0 missed (100%) salience_recency: 53 mutants, 48 caught, 3 missed (96%) recall_cache: 25 mutants, 18 caught, 5 missed (80%) Refs: nginjen mutation testing pattern (#mutation-testing) Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…ion-killing tests (score 50%→97%) (#1024) Two production bugs found by mutation testing: 1. Heading duplication: oversized markdown sections had their heading prepended twice in the first sub-chunk (once by split_by_headings, once more by the sub-chunk loop), corrupting downstream embeddings. Fixed by removing the dead heading_prefix re-prepend path entirely. 2. Multibyte infinite loop: split_long_text's zero-progress guard advanced by raw byte offsets that could land inside multi-byte UTF-8 characters (CJK/emoji), flooring back to start forever. chunk_markdown("日本語", 2) would hang. Fixed with a proper forward-char-boundary advance in the guard. Also: - 40 new mutation-killing tests (chunker: 20 → 60 tests) - cargo-mutants config moved to .cargo/mutants.toml (v27 schema: exclude_globs/exclude_re, valid keys only); glob paths fixed to match from workspace root; CLI update_check also excluded - 3 proven-equivalent mutants excluded with documented reasoning - docs/mutation-testing.md: final scores, bug postmortem, timeout notes Verify run: 164 mutants, 149 caught, 0 missed, 5 timeout, 10 unviable (97%) Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
Patch release: chunker heading duplication fix, multibyte infinite loop fix, mutation testing hardening (PR #1024). - Bump workspace version 0.14.0 -> 0.14.1 - Bump intra-workspace deps (cli/mcp/server -> core 0.14.1) - CHANGELOG entry for 0.14.1 Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…1030) * fix(cli): embed bundled assets inside crate root for crates.io publish include_str! paths pointed outside the package root (../../../.agents, ../../../extensions). cargo publish cannot bundle files outside the package, so uteke-cli has failed to publish since June (stuck at 0.4.3) while the release workflow hid the failure with continue-on-error. Assets now live in crates/uteke-cli/assets/ and ship inside the .crate. Verified locally: cargo package -p uteke-cli passes with full verify. * ci(release): verify crates.io versions after publish The continue-on-error on publish steps (added for the tag race fix) also hides genuine publish failures. Verify all four crates report the tagged version on crates.io; fail the job if any crate lags behind. * style: cargo fmt --------- Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
The v0.14.2 release reused the v0.13.0 notes because a committed RELEASE_NOTES.md is preferred over auto-generation and nothing checked which version it was for. The release shipped with wrong notes until manually fixed. The guard now requires the file to mention the tagged version. Also remove the stale v0.13.0 file so the next release auto-generates from the CHANGELOG unless someone writes fresh notes. Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
, #1035) (#1038) * fix(server,mcp): default recall strategy to hybrid, validate strategy at boundary (#1034, #1035) HTTP: resolve strategy once (request > [recall] default_strategy config > hybrid). Invalid strategy returns 400 on all paths (bare recall, unified search, v1). The eager legacy recall that ran before strategy resolution is removed. Memory-only recall path routes through recall_hybrid with the same 3x over-fetch post-filter pattern used by recall_unified_memories (entity/category filters). MCP: uteke_recall schema exposes strategy (vector|fts5|hybrid|graph). Default resolves to hybrid, invalid values return a loud JSON-RPC error (-32603) instead of silently falling back to vector. Server startup: sanitize [recall] default_strategy from uteke.toml — invalid value warns and falls back to hybrid so a config typo cannot 400 every request with a message blaming the request. Docs: api-reference.md and mcp.md now describe the hybrid default, HTTP 400 on invalid, and the config fallback chain. Verified empirically against a scratch store: HTTP matrix 10/10, MCP harness 8/8 including engine parity (default == hybrid, warm cache) and loud bogus rejection. * docs: regenerate api-reference via docgen (strategy hybrid default, 400 on invalid) --------- Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…plate file (#1041) * fix(ci): replace unquoted heredoc in release notes generation with template file The unquoted heredoc << RELEASEEOF underwent shell expansion: markdown backticks became command substitutions and executed on the runner. v0.14.3 release: installer curl|sh ran, uteke-serve --port 8767 started and blocked the job for 48 minutes (2 runs, deterministic). Move the static tail (downloads table + quick start) to scripts/release-notes-template.md and substitute __VER__ via sed. Zero shell expansion over markdown content. Validated locally: generate completes instantly, output byte-correct. Fixes the release pipeline for all future releases. * ci: re-trigger review bots (LLM API transient error) --------- Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…pages, fix release-notes asset names (#1055) * docs: sync stale version refs, roadmap v0.13–v0.14, merge comparison pages, fix release-notes asset names (#1043) - README/README.id/AGENT.md/install.md: version + test-count updates (0.14.3, 530+ tests); AGENT.md now lists all 5 workspace crates incl. docgen - docs/roadmap.md: add v0.13.0–v0.13.2 and v0.14.0–v0.14.3 entries from CHANGELOG - comparison: merge comparison-2026.md (canonical long-form) into comparison.md, keep at-a-glance matrix + extraction table from the old page, drop dead /blog link - extensions/hermes-memory-provider → extensions/hermes-uteke-memory (+ embedded assets copy and all path refs) — dir name no longer references the removed Mode B provider - scripts/release-notes-template.md: download table filenames get v prefix to match actual release assets (#1043) * fix(release): keep v prefix on pinned-install example in release notes template Co-authored-by: VIVAAN-DHAWAN <VIVAAN-DHAWAN@users.noreply.github.com> Same fix as #1046 — UTEKE_VERSION is used verbatim as the release tag by install.sh, so the pin example needs the v prefix too. Picking it up here so #1043 closes fully through this PR once the rest of the audit lands. * Revert "fix(release): keep v prefix on pinned-install example in release notes template" This reverts commit 222d067.
…1056) Agents are advisory-mode by default: audit/check requests are not authorization to mutate. Gate push/merge/cherry-pick from others' PRs, comments/edits on others' PRs/issues, PR retargeting, and anything irreversible or externally visible behind explicit maintainer approval. Correct flow: analyze → present options → wait → execute approved scope → report. Real incident 2026-08-17 documented as cautionary example.
All id call sites across uteke-core (remember, chunks, graph, edges, timeline, orphans, import) now mint UUIDv7. Storage and wire format unchanged (TEXT uuid); v4/v7 coexist — existing stores open unchanged. Adds two tests: v7 version nibble + ascending order for consecutive mints, and v4/v7 parse coexistence.
#1047) (#1061) Root cause was NOT a partial delete — soft_forget() correctly marks the row deprecated and removes the vector. The leak was read-side: - list()/search_content() never filtered deprecated rows → ghosts in 'uteke list' after forget - store.count(None) counted deprecated rows → doctor/verify reported permanent DB/Index MISMATCH after any soft-forget - load_all() (repair/verify source) included deprecated rows → repair() re-added soft-deleted vectors to the index, resurrecting them in recall Fix, uniform active-only contract: - list() (all 4 branches) + search_content(): AND deprecated = 0 - load_all(): AND deprecated = 0 (recompute_importance also stops wasting cycles on hidden rows) - count(): active-only for both None and Some(ns) paths — directly comparable to index.len() in doctor/verify - new count_all(): explicit include-deprecated totals for reporting Regression test: isolated temp-dir store (':memory:' stores resolve the vector index to a CWD file and cross-contaminate parallel test runs — that shared uteke_index.usearch pollution is also why this bug hid). Asserts soft-forgotten id absent from list/list-ns/load_all and doctor reports no MISMATCH.
…ace keyword misses (#1051) (#1062) uteke_search / CLI search go through store.search_content() (LIKE substring), which mapped namespace=None to the 'default' namespace instead of searching across all namespaces like list(None) does (#526). Memories stored in any other namespace were invisible to keyword search unless the caller explicitly passed that namespace. - namespace=None now searches across all namespaces (deprecated rows still excluded) - hyphenated identifiers keep working as literal substrings on the LIKE path (pinned by test), and the FTS5 phrase path already handles them (unicode61 adjacency) — probed both in-test Regression test: cross-namespace hit via None, scoped hit via Some(ns), full and partial hyphenated identifier matching.
… parity (#1037) (#1063) recall_hybrid() applied salience/recency boosts only on the cache-miss path; the cache-hit early return handed back raw cached scores. Same query + strategy returned different scores cold vs warm (delta ~0.13). Three-part fix: - Cache-hit read path now re-applies boosts (boost → sort → truncate → min_score), identical post-processing to the miss path via shared apply_salience_recency_boosts() helper. Cache intentionally stores RAW scores — boosts are time-dependent and re-applied per read. - Boost window: cache stores limit*4+16 candidates computed with min_score=0, so boosts can lift a memory from outside the raw top-N into the final top-N on warm reads too (cora finding — cached top-limit-only set would permanently exclude boostable candidates). - min_score thresholding now happens AFTER boosts on BOTH paths (previously miss-path Vector/Fts5 filtered raw scores while hit-path filtered boosted scores). Tests: cold/warm score parity (fails with max delta 0.125 pre-fix); boost-reorder-across-limit; noop-config warm hits respect limit.
…umented (#1036) (#1064) ExportEntry had no namespace field — every exported row lost its namespace, so export→import collapsed multi-namespace stores into one. Also root-caused the reporter's 36-row delta: export() reads load_all() which filters deprecated=0 (soft-deleted rows excluded by design, but undocumented). - ExportEntry gains 'namespace' (serde default 'default' keeps old export files importable) - export() serializes m.namespace on every row - import(): caller-supplied namespace = explicit override for all rows; without it, per-row namespace wins → round-trips reconstruct namespaces - docgen/api-reference regenerated (no route change, freshness check) Test: 3-namespace × 2-row export shape (every row carries its ns), parse-side attribution preservation, legacy-row default fallback.
…confirm to apply (#1050) (#1065) A no-args uteke_dream call ran the maintenance pipeline with dry_run=false against ALL namespaces while the tool description said 'Safe to run periodically'. Real incident: single exploratory call mutated 4,067 rows store-wide. - dry_run defaults to TRUE — a no-args call can only preview - Applying requires explicit dry_run=false - Unscoped applying runs refused unless confirm_large=true (two-flag decision for whole-store maintenance) - Large-batch guard: applying runs projecting >100 changes refuse without confirm_large=true; preview computed first, so the refusal reports the projected count - Output announces scope ([SCOPE: ALL NAMESPACES]) on both preview and apply paths - Description rewritten: states destructive nature + dry-run-first guidance instead of 'Safe to run periodically' - docs/mcp.md tool table updated
…doc_search scores, room ids (#1052) (#1066) - uteke_stats: multi-line output — tiers (hot/warm/cold), pinned + deprecated split, recall cache hits/misses, and per-namespace breakdown when unscoped (was: single 'Total | Tags | DB' line) - uteke_doc_search: per-result score, matched chunk heading + 120-char snippet (ranking was invisible; results were bare slug — title) - uteke_room_memories: lines now include 8-char short id so the next tool call (pin/forget/graph edges) can act on the row - uteke_room_recall description states the existing-room_id precondition instead of failing only at call time - core: Store::count_pinned() + Uteke::{count_pinned, count_deprecated, namespace_counts} accessors (store field is private; MCP uses the public API surface)
…l ID-taking tools (#1048, #1049) (#1067) MCP printed 8-char short ids in recall/list but every ID-consuming tool required the full UUID (exact SQL match) — the basic loop recall → act silently no-opped. resolve_id_prefix() existed in core (#794); MCP never wired it. - resolve_id() helper: full UUID passes through; any shorter prefix resolves via resolve_id_prefix(); ambiguous prefixes error loudly; unknown prefixes error instead of silent not-found - Wired into uteke_forget, uteke_pin, uteke_unpin, uteke_graph_add_edge, uteke_graph_remove_edge - NEW uteke_get {id} — full record by id/prefix, no truncation (fills the read-by-id gap; recall/search return ranked excerpts) - NEW uteke_update {id, content?, tags?, metadata?, importance?, pinned?, memory_type?} — partial-update semantics matching HTTP PUT /memory (#659); content change re-embeds - Tool schemas + docs/mcp.md: id params documented as UUID-or-prefix Tests (5, scratch temp-dir stores): prefix + full + unknown resolution; uteke_get full record via short id; pin via short id; forget short id happy path + bogus prefix errors loudly; update partial fields leave content untouched.
…lt (#1078 P0) (#1152) Six-case sequential test covering the full precedence chain. Mutation proof: all 4 mutants of resolve_namespace caught (body-replace x2, delete-! on empty-env guard, != -> == on config fallback), run with cargo-mutants 27.1.0 scoped to main.rs. Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…ary matrix (#1078 P0) (#1153) - config.rs: 8 tests covering score range guards (out-of-range ignored), strategy validation (valid accepted / invalid keeps fusion default), graph weights (valid + out-of-range ignored), embed fallback env overrides (all fields + is_configured matrix: none/partial/full) - lib.rs: validate_input_with_limits boundary matrix — empty content, at/over content limit, limit=0 disables check, tag count at/over, empty tag rejected, tag length at/over, limit=0 disables tag check Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…issing-key (#1078 P0) (#1154) - unknown backend: rejected eagerly at open() with clear message - custom backend without embedder: immediate Validation error - openai backend without API key: deterministic init error, 2nd call hits the #822 transient cache (asserts 'previously failed (cached)') All deterministic — no network calls. Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…set_namespace_in_toml (#1078 P0) (#1155) Targets the cargo-mutants survivors from the config.rs run (48% score): - migrate_content: store_path/namespace → [store], model/max_seq_length → [embedding], unknown keys + section headers pass through - global_config_path: honors UTEKE_HOME env override - set_namespace_in_toml: rewrites only [store].namespace (not [other]), inserts directly after [store] header, appends section when missing, replaces existing value in place 78 mutants → 40 missed before this batch; these tests kill the migrate_content arm-deletes, the != / && section-boundary mutants, and the insert-position mutants. Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…hmarks, bundled skill bump (#1156) * docs: refresh README (EN+ID) for 0.16.0 — fusion default, 500Q benchmarks, new features * docs: align strategy/version references with 0.16.0 defaults (fusion, install pins) * docs(roadmap): record v0.15.0 and v0.16.0 as released * docs: correct 500Q results-availability claim (aggregates in repo, raw on volume) * chore(skill): bump bundled uteke-memory-skill to 0.16.0 (fusion default, lifecycle, onboard) --------- Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* feat: add full memory detail fields to UnifiedSearchResult
UnifiedSearchResult now includes memory_type, namespace, source,
source_type, importance, pinned, access_count, last_accessed,
created_at, updated_at.
- Memory results: all fields populated from Memory struct
- Document results: all memory-specific fields are None (skip_serializing_if)
- Tests verify both memory fields present and document fields omitted
- CLI print_unified_human shows new fields (type, source, importance, pinned, access count)
Fixes the gap where recall with search_type=all/memory/doc returned
less metadata than plain recall.
* fix: remove O(n) reverse scan in get_related() + fix recall_room() missing columns
A) get_related() in graph.rs was doing load_all(None) to find reverse
metadata relationships — O(n) full table scan. The edge_bfs() method
already handles bidirectional lookups via UNION on source_id/target_id
using indexed SQL. Removed the full-table scan. Forward-only metadata
fallback kept for pre-v8 stores (O(degree), not O(n)).
B) recall_room() SQL in memory/rooms.rs was missing m.slug, m.source,
m.source_type columns (only selected up to content_type=col16).
row_to_memory() expects 20 columns (0-19). Fixed all 4 SQL variants.
Refs #689
* feat: room↔document junction table (schema v15, #689)
New room_documents table with room_id + doc_slug composite PK.
Enables explicit room-to-document linking beyond memory wikilinks.
Store methods:
- room_add_document(room_id, doc_slug)
- room_remove_document(room_id, doc_slug)
- room_list_documents(room_id) → Vec<slug>
- document_list_rooms(doc_slug) → Vec<room_id>
Server endpoints:
- PUT /room/document/add
- DELETE /room/document/remove
- POST /room/document/list
- POST /doc/room/list
Refs #689
* fix: use public Uteke wrappers instead of private store field
- Add room_add_document, room_remove_document, room_list_documents,
document_list_rooms to Uteke public API (rooms.rs)
- Replace uteke.store.xxx with uteke.xxx in server handlers
- Add missing ns binding in /tags GET handler
- Apply cargo fmt
* feat: memory↔document cross-entity linking via [[doc-slug]] wikilinks
When [[slug]] in memory content doesn't match any memory, Uteke now
checks if it matches a document slug. If so, creates a 'references_doc'
edge (auto-backlinked as 'referenced_by').
New API:
- POST /memory/doc-refs → document slugs referenced by a memory
- POST /doc/mem-refs → memory IDs referencing a document
- Uteke::recall_memories_for_document(slug)
- Uteke::recall_documents_for_memory(memory_id)
- Store::edge_targets() / edge_sources() — generic typed edge queries
UnifiedSearchResult enriched with:
- linked_doc_slugs (for memory results)
- linked_memory_ids (for document results)
Refs #689
* fix: use read_body + typed deserialization, add missing ns binding
- Replace read_json() (nonexistent) with read_body<T>() pattern in
/memory/doc-refs and /doc/mem-refs handlers
- Add missing let ns = parse_query_namespace(&path) in /tags handler
- Apply cargo fmt
* fix: use serde_json::json! instead of bare json! macro
The handlers used json!() which is not in scope — must be
serde_json::json!() to match the rest of handlers.rs.
* chore: remove bmad/wds skills, update docs for v0.7.3→v0.7.4 changes
- Remove _bmad/ folder (BMAD config/templates, not uteke-related)
- Remove .agents/skills/bmad-* (48 BMAD skill folders, ~14MB)
- Remove .agents/skills/wds-* (14 WDS skill folders, ~5MB)
- Remove .agents/skills/memory + sync (WDS session skills)
- Keep .agents/skills/uteke-memory (legitimate uteke skill, bump 0.6.7→0.7.3)
Docs updates (v0.7.3→HEAD):
- architecture.md: schema v13→v15, new endpoint tables (Memory Mutation,
Room↔Document Junction, Cross-Entity References), room_documents info
- cli-reference.md: 9 new HTTP endpoints documented
- mcp.md: tool count 27→29, add graph_add_edge + graph_remove_edge
- CHANGELOG.md: populate [Unreleased] with all changes since v0.7.3
- README.md: add Partial Updates + Cross-Entity Linking features
* docs: fix Mode C handler — replace /proc with cwd-based agent resolution
The /proc/self/cmdline approach is unreliable because shell hooks run
as child subprocesses whose cmdline is the handler script, not the
gateway process. This caused agent name resolution to fail when hooks
were invoked from asyncio contexts (e.g., session expiry watcher).
Changes:
- Replace /proc/self/cmdline with cwd-based resolution from payload
- Dynamically resolve agent per invocation (not at module load time)
- Remove hardcoded UTEKE_BIN path check (assume uteke on PATH)
- Remove cron session skip (handler-level concern, not docs example)
- Pass agent name to recall function explicitly
* docs: fix docstring — remove false HERMES_PROFILE fallback claim
Cora review: docstring claimed 'Priority: cwd > HERMES_PROFILE env > fallback'
but code only checks cwd then returns 'default'. Fix docstring to match
actual behavior.
* docs: de-emphasize deprecated Mode B across Hermes integration docs
Mode B (memory-provider) was removed for Hermes on 2026-06-29 but the
docs still presented it as an active option alongside Mode A and C.
Changes:
- Architecture diagram: label Mode B as deprecated, add Mode C
- Comparison table: strikethrough Mode B column, reorder A/C first
- Mode B section: collapse 96 lines of deprecated content into 10-line
blockquote with migration links
- Mode comparison summary: same strikethrough treatment
- Requirements: remove Mode B entry
- Non-Hermes agents section: unchanged (already has Hermes warning)
Result: 527 → 446 lines (-81 lines). Active modes (A+C) are now the
primary focus; Mode B is preserved as historical reference only.
* feat: add `uteke onboard` — interactive onboarding wizard
Guides new users from zero to productive in one command:
- Detects install (binary on PATH, existing store)
- Asks which AI agent they use (Hermes/Claude/Cursor/Pi/OpenCode)
- Picks integration mode (tool vs memory-provider)
- Toggles features on/off (Aging, Auto-maintenance, Graph rerank,
Salience/Recency boost, Server mode)
- Writes uteke.toml config with selections
- Runs uteke init with selected agent automatically
- Prints a full feature showcase (all commands grouped by category)
Non-interactive mode: uteke onboard --yes --agent hermes --namespace default
Docs updated: getting-started.md, cli-reference.md, AGENT.md, CHANGELOG.md
New dep: which crate for PATH detection
* docs: add onboarding section to README.md and README.id.md
Add a prominent "🚀 Onboarding" section right after install, before
"Why Uteke?". Includes an agent-readable callout so when a user drops
the GitHub link to an AI agent (Hermes, Claude, etc.), the agent reads
the README and knows to run `uteke onboard.
* fix(docs): repair dangling Mode B anchor link
Rename of the Mode B heading (-> '~~Mode B~~ — memory-provider (deprecated)')
orphaned the cross-reference at line 245. Update the link target to the new
anchor #mode-b--memory-provider-deprecated.
Verified via github-slugger: 0 broken internal links.
* fix(docs): clean fragile '575577' anchor on Memory-Provider section
The heading 'Memory-Provider for Other Agents (#575/#577)' produced a
confusing concatenated anchor #memory-provider-for-other-agents-575577.
Drop the issue refs from the heading (move into the body) so the anchor
becomes the clean #memory-provider-for-other-agents. Fixes CodeCora alert.
Verified via github-slugger: 0 broken links; no other file references the
old anchor.
* fix(server): match Error::Validation instead of string in /memory/importance
The /memory/importance handler classified validation errors via
e.to_string().contains("importance must be"), coupling the 400/500
status to the exact wording of the validation message in store.rs.
If that wording changes, a 400 would silently degrade to a 500.
Match the typed Error::Validation(_) variant instead, mirroring the
pattern already used by PUT /memory.
Closes #697
* fix(core): validate room_id + doc_slug exist in room_add_document
The room_documents junction table has an FK on room_id (→ rooms) but
none on doc_slug, so POST /room/document/add accepted any doc_slug and
created dangling room→document links. A bogus room_id also surfaced as
a raw FK violation → 500.
Validate both room_id and doc_slug existence in room_add_document,
returning Error::Validation (→ 400) for unknown values. Update the
PUT /room/document/add handler to map Validation → 400 accordingly.
Closes #698
* fix: address PR #696 review feedback
🔴 Must Fix:
- AGENT.md: correct schema version v13 → v15 (room↔document junction, #689)
- onboard.rs: remove misleading get_stats() placeholder, drop count claim
- onboard.rs: write_config() now backs up existing uteke.toml to .bak and
prompts for confirmation before overwriting (prevents credential loss)
🟡 Should Fix:
- prompt_integration_mode(): print message for custom agents (no longer silent)
- validate_agent(): now warns on unrecognized agents instead of being a no-op
- Banner boxes: replace misaligned Unicode box drawing with consistent ASCII
print_banner() helper (fixes off-by-1 alignment, ambiguous em-dash width)
- --json flag: intentionally ignored in onboard to avoid mixing ASCII banners
with parseable JSON output
🟢 Nice to Have:
- Install URL: main → develop (matches repo default branch convention)
- Add 7 unit tests for pure functions (get_toggle, validate_agent, print_banner,
write_config TOML structure)
All tests pass: cargo fmt --check ✅, cargo clippy -D warnings ✅, cargo test (50 passed) ✅
Co-authored-by: ajianaz <reviewer>
* test: room operations test suite (#689 PR5)
Add comprehensive test coverage for room operations across two files:
memory/rooms.rs (33 tests, Store-level):
- CRUD: create, get, list (all + namespace filter), delete, duplicate error, cascade
- room_memories: link, idempotent link, updated_at propagation, recall with limit,
author filter, empty room, limit=0 (all), author metadata enrichment
- get_room_memory_ids: with/without author filter
- room_stats: with memories (counts, participants, last_activity) + empty + nonexistent
- room_summary: empty room, with memories (clusters, tags, time_range), decisions,
pinned highlights, nonexistent
- room_document: empty room, with memories (pinned section, type sections), nonexistent
- room_documents junction: add+list, idempotent add, remove, reverse lookup (document_list_rooms)
rooms.rs (14 tests, Uteke-level):
- CRUD roundtrip, namespace filter, delete
- room_stats (empty + nonexistent), room_summary (empty)
- room_document (empty + nonexistent)
- room_documents junction: add+list+remove, idempotent, reverse lookup
- remember_in_room + recall_room (4 tests, #[ignore] — requires ONNX embedder)
* fix: add missing Store import in rooms test module
* fix: resolve CI failures — unused imports + document validation in room tests
- Replace glob import with explicit imports (Room, Document)
to fix clippy unused-import error in both test modules
- Add helper for creating valid documents
- Pre-create documents via before attaching to rooms
in 4 junction-table tests (room_add_document validates slug existence)
* style: rustfmt fixes
* fix: remove unused Room import + move junction tests to Store-level
- Remove unused super::Room import (clippy error)
- Move room↔document junction tests from rooms.rs to memory/rooms.rs
because Uteke::doc_upsert requires ONNX embedder, but Store-level
upsert_document works without it. Coverage is maintained.
* feat(theme): adopt @codecora/theme — Catppuccin Mocha, new base /uteke/docs/
- Replace custom theme with @codecora/theme (single-source Catppuccin tokens)
- Migrate config.ts to createConfig() helper
- Update base path: /docs/uteke/ → /uteke/docs/ (new routing scheme)
- Remove custom.css (now handled by shared theme)
- Accent: green
* feat: add enrich parameter to recall_unified for cross-entity linking (#689)
Add `enrich: bool` parameter to recall_unified() that populates
linked_doc_slugs and linked_memory_ids on UnifiedSearchResult using
the existing EDGE_REFERENCES_DOC infrastructure.
- Add enrich_memory_doc_links() and enrich_doc_memory_links() helpers
that look up cross-entity edges and populate the appropriate fields
- Wire enrichment into recall_unified dispatcher (Memory/Document/All)
- Add enrich field to RecallRequest in HTTP API (default: false)
- Pass enrich through from server handler to recall_unified
- Update CLI and MCP callers with enrich=false for backward compat
- Add 3 integration tests (all #[ignore] -- require ONNX embedder)
Files modified:
- crates/uteke-core/src/lib.rs: signature, helpers, tests
- crates/uteke-server/src/types.rs: RecallRequest.enrich field
- crates/uteke-server/src/handlers.rs: pass enrich to recall_unified
- crates/uteke-cli/src/commands/recall.rs: backward compat
- crates/uteke-mcp/src/lib.rs: backward compat
* feat: add RoomSummary.referenced_documents and room_summary_with_docs (#689 PR3)
- Add referenced_documents field (Option<Vec<String>>) to RoomSummary struct
with serde(default, skip_serializing_if) for backward compatibility
- Set referenced_documents: None at both RoomSummary construction sites
in room_summary() for backward compat
- Add Store::room_summary_with_docs() that enriches a summary with
document slugs from the room_documents junction table
- Add Uteke::room_summary_with_docs() high-level wrapper in rooms.rs
- Add 3 tests: with documents, empty room (None), nonexistent room (None)
* feat(cli): add --enrich flag for cross-entity recall links (#689 PR4)
Wire up the enrich parameter added by PR702 so users can opt into
cross-entity link resolution via uteke recall --enrich.
- Add --enrich bool flag to Recall subcommand in cli.rs
- Thread enrich from CLI dispatch → run_recall → recall_unified()
- Display linked_doc_slugs (📄 Docs) on memory results in human output
- Display linked_memory_ids count (🔗 Memories) on document results
- MCP recall left at enrich=false for now (separate enhancement)
* fix(theme): pin to codecora-theme with config.mjs (Node 24 compat)
* test: cross-entity integration tests (#689 PR6)
Store-level tests (no embedder needed, run in CI):
- cross_entity_doc_edge_round_trip: memory↔document edge CRUD
- cross_entity_doc_edge_with_backlink: backlink auto-generation
- cross_entity_doc_edge_idempotent: INSERT OR IGNORE dedup
- cross_entity_resolve_nonexistent_doc_slug_returns_none
- cross_entity_edge_targets_empty_for_no_edges
Room↔Document junction tests:
- room_doc_enrichment_full_flow: full link→list→summary→reverse
- room_doc_remove_unlink: unlink clears both directions
- room_doc_nonexistent_doc_rejected: Validation error
- room_doc_nonexistent_room_rejected: Validation error
- room_doc_shared_across_multiple_rooms: shared doc
E2E tests (#[ignore] = requires ONNX embedder):
- e2e_wikilink_creates_doc_edge_and_enriches: full wikilink→edge→enrich
- e2e_room_doc_memory_cross_entity: three-entity cross-linking
Closes #689
* style: rustfmt fixes for integration tests
* fix(theme): update codecora-theme pin (JSON import assertion fix)
* fix(docs): add ignoreDeadLinks to prevent broken link check failure
* fix(theme): pin codecora-theme to latest main (forward opts fix)
* docs: restructure — split getting-started, add install/comparison/feature pages
- Extract install.md from getting-started (curl, cargo, binary, docker)
- Extract rooms.md, time-travel.md, smart-decay.md, relationship-graph.md
- Extract benchmarks.md (from BENCHMARKS.md), add shell-hooks.md
- Add comparison.md with full feature comparison table
- Slim down index.md (ringkas 5-row comparison, link to /comparison)
- Update config.ts sidebar (halaman terpisah, bukan anchor links)
- Remove old BENCHMARKS.md (merged into benchmarks.md)
Install routes updated in CF Worker:
/{product}/install -> 302 -> raw GitHub install.sh
/install (legacy) -> 302 -> uteke install.sh
* feat: enable salience/recency boosts by default (opt-out) (#721)
Change SalienceRecencyConfig::default() weights from 0.0 to 0.1.
- Both salience and recency boosts are now ON by default (weight 0.1)
- CLI: absent flag = default 0.1, --salience/--recency = config 0.15,
--no-salience/--no-recency = 0.0 (opt-out)
- Server/API: inherits the 0.1 default automatically
- Boost is additive and small enough to not dominate embedding similarity
- Adopted from Hermes holographic memory analysis
Breaking: none — the boost is additive and tiny (max +0.1).
Backward compatible: users can disable via --no-salience --no-recency.
* feat: Jaccard token similarity as post-RRF reranking signal (#719)
Add Jaccard token overlap as an orthogonal recall signal (#719):
- New jaccard.rs module with tokenize() and jaccard_similarity()
- Post-RRF additive boost when jaccard_weight > 0.0
- CLI config [recall].jaccard_weight (default 0.0 = off)
- Setter API: Uteke::set_jaccard_weight()
- Tag tokens included in content token set for richer overlap
Jaccard catches different cases than BM25 (IDF-weighted) and vector
cosine (semantic). When enabled (recommended 0.10-0.15), it boosts
results with high token overlap regardless of term rarity or
embedding distance.
Closes #719
* fix(docs): deprecate hermes-memory-provider for Hermes + add HTTP transport
- README: add deprecation notice for Hermes (removed 2026-06-29),
clarify template is for pi/claude/cursor agents only
- README: add transport mode section (subprocess vs HTTP)
- plugin.yaml: bump to v1.1.0, add deprecation comment
- __init__.py.tmpl: add dual transport support (subprocess + HTTP),
add UTEKE_SERVER_URL/UTEKE_TOKEN config, refactor recall/remember
into separate _subprocess/_http methods, extraction guard for missing binary
- docs/integrations/hermes.md: link to extension template source in Mode B section
* feat: trust scoring with feedback API (#718)
Add helpful/unhelpful feedback mechanism to adjust memory importance:
Core (uteke-core):
- Uteke::feedback_helpful() → importance += 0.05
- Uteke::feedback_unhelpful() → importance -= 0.10
- Asymmetric: penalty > reward (adopted from Hermes trust scoring)
- Clamped to [0.0, 1.0], returns new importance value
CLI:
- uteke feedback <id> helpful|unhelpful
- JSON and human-readable output
- FeedbackAction enum (Helpful, Unhelpful)
Server API:
- POST /memory/feedback { id, feedback }
- Validates UUID, returns { id, feedback, delta, importance }
Closes #718
* style: fix cargo fmt formatting (#722)
* style: fix cargo fmt formatting (#723)
* fix: use raw SQL in feedback_adjust instead of non-existent store.get() (#725)
* fix: import FeedbackAction in commands/mod.rs (#725)
* fix: avoid redundant reference in feedback command (#725)
* feat: auto-contradiction scan as Dream pipeline phase (#720)
Add Phase 4 (Contradict) to the Dream maintenance pipeline.
Algorithm:
- Load top 200 most recently updated memories (O(n²) bounded)
- For each pair: check tag overlap (Jaccard ≥ 0.3) + embedding
cosine similarity (≤ 0.6 threshold)
- High tag overlap + low content similarity = contradiction
- Creates graph_edges with relation 'contradicts' (older → newer)
Pipeline order: Lint → Backlinks → Dedup → **Contradict** → Orphans → Compact → Verify
Supports dry-run mode (reports without creating edges).
CLI: uteke dream --phases contradict
Closes #720
* fix: use GraphStore::new for add_edge in contradict phase (#726)
* style: fix cargo fmt formatting for contradict phase (#726)
* fix: prefix unused ns variable with underscore in load_recent_memories (#726)
* docs: update CHANGELOG, CLI reference, and architecture for v0.7.4 features
- CHANGELOG: 3 Added entries (trust scoring, jaccard, contradiction)
2 Changed entries (salience default-on, dream 7 phases)
- cli-reference: updated recall flags (--salience/--recency default-on,
--jaccard), dream phases (contradict), new feedback command + HTTP endpoint
- architecture: salience opt-out, dream pipeline 7 phases
* chore: release v0.8.0 — version bump, CHANGELOG, and docs update
- Bump workspace version 0.7.3 → 0.8.0
- Update all sub-crate internal deps to 0.8.0
- Finalize CHANGELOG: add missing PR entries, new Deprecated and Dependencies sections
- Rename [Unreleased] → [0.8.0] with 2026-07-17 date
- Update docs version refs: cli-reference, install, architecture (v0.7.4 → v0.8.0)
* refactor(api): rename POST /room/document → POST /room/summary (#735)
BREAKING: The room summary document endpoint is renamed from POST /room/document
to POST /room/summary to eliminate confusion with the room↔document junction
routes (/room/document/add, /room/document/list, /room/document/remove).
Changes:
- Server: POST /room/summary is the new primary endpoint
- Server: POST /room/document retained as deprecated alias with warn! log
- Core: room_document() → room_summary_document() across all crates
- CLI: 'uteke room document' command unchanged (help text updated)
- MCP: uteke_room_document tool updated to use new function
- Docs: cli-reference, docker, mcp, rooms updated
Closes #735
* fix: rename to POST /room/summary-document — avoid collision with /room/summary
Clippy caught unreachable pattern: POST /room/summary already used by
room_summary() (tag clustering). Renamed to POST /room/summary-document
to eliminate ambiguity with both junction routes AND the clustering endpoint.
Refs #735
* feat(api): URL prefix versioning /api/v1 and /api/v2 (#737)
Adds API versioning middleware to uteke-server.
Changes:
- ApiVersion enum (V1, V2) with path prefix parser
- /api/v1/* routes return v1 format (flat recall results for backward compat)
- /api/v2/* routes return current v2 format (wrapped UnifiedSearchResult)
- Unversioned routes (/recall, /remember, etc.) continue to work (→ latest)
- Health endpoint now includes api_versions and api_latest fields
- to_v1_flat() adapter converts UnifiedSearchResult → v1 flat format
v1 vs v2 differences:
- v1 recall: [{id, content, score, namespace, tags, ...}] (flat)
- v2 recall: [{memory: {id, content, ...}, score}] (wrapped)
Routes affected:
- /recall → /api/v1/recall (flat) or /api/v2/recall (wrapped)
- /remember, /memory, /forget, /list, /search, /stats, etc.
- /room/*, /doc/* — all sub-routes versioned
- /health — unversioned, includes version info
* feat(config): configurable dream pipeline thresholds (#731)
Replaces hardcoded dream pipeline constants with configurable values.
Changes:
- DreamConfig struct (uteke-core + uteke-cli config)
- contradict_similarity_threshold: 0.6 (cosine > this → NOT contradiction)
- contradict_tag_jaccard_min: 0.4 (up from 0.3, reduces false positives)
- contradict_max_memories: 200 (O(n²) scan limit)
- dedup_threshold: 0.92 (cosine > this → merge candidate)
- orphan_importance_threshold: 0.15 (safer than old 0.3)
- MaintenanceConfig defaults changed to safer values:
- auto_aging_enabled: true → false (opt-in)
- auto_aging_interval_hours: 6 → 24 (daily)
- auto_dream_interval_days: 3 → 7 (weekly)
- DreamConfig wired into CLI main.rs and server main.rs via set_dream_config()
- dream.rs reads from self.dream_config instead of const values
- DEFAULT_ORPHAN_THRESHOLD updated 0.3 → 0.15
- Server reads [dream] section from uteke.toml
Config example:
[dream]
contradict_similarity_threshold = 0.6
contradict_tag_jaccard_min = 0.4
contradict_max_memories = 200
dedup_threshold = 0.92
orphan_importance_threshold = 0.15
Closes #731
* fix: resolve type mismatch and format issues (#731)
- consolidate() expects f32 not f64 — remove incorrect cast
- Server dream config: unwrap_or_default() for Option<usize> returns 0
(wrong), use unwrap_or(DreamConfig::default().*) instead
- Format: restructure server dream config block for rustfmt compliance
* fix(format): collapse consolidate call to single line per rustfmt
* fix(onboard): resolve clippy errors in test code
- Remove unused 'toggles' vec in test_write_config_generates_valid_toml
- Replace useless format! with raw string literal
- Remove stray closing parenthesis
* fix(core): robust embedding model download with timeout, retry, streaming (#740)
- Add connect timeout (30s) and read timeout (300s) to prevent
infinite hangs on slow/unstable connections
- Retry up to 3 attempts on transient failures with cleanup
- Stream download to disk via 64KB chunks instead of buffering
entire 187MB model_data file in RAM
- Add progress indicator (file size + percentage milestones)
- Add human-readable byte formatting (KB/MB/GB)
- Verify content-length matches downloaded bytes
* fix: use checked_div for clippy compliance (#744)
* docs: add SECURITY.md and PR template
- SECURITY.md: supported versions, vulnerability reporting process, SLA
- PULL_REQUEST_TEMPLATE.md: type checklist, CI verification, conventional commits
* fix: use GitHub Private Vulnerability Reporting instead of public issue
Cora caught that instructing reporters to open a public issue with
[SECURITY] prefix contradicts the 'do not open a public issue' policy.
GitHub issues are public by default — only Private Vulnerability Reporting
provides proper confidentiality.
* fix(core): prevent os error 33 on Windows by reading usearch index from locked file handle
* style(core): apply cargo fmt to fix CI check
* fix(core): filter deprecated memories in recall() (#748)
recall() vector search path was missing a deprecated filter, causing
deprecated memories to appear in search results (61.7% in reported case).
FTS5 path already filters at SQL level (m.deprecated = 0).
recall_at_time() already filters in post-filter.
Adding the filter in recall() fixes all downstream paths:
- Vector strategy
- Hybrid RRF (calls recall())
- Graph rerank (calls recall_rrf -> recall())
* chore: release v0.9.0 — version bump, CHANGELOG
v0.9.0 features:
- uteke onboard interactive wizard
- API URL versioning /api/v1, /api/v2
- Configurable dream pipeline thresholds
- SECURITY.md + PR template
v0.9.0 fixes:
- Deprecated memories in vector search (#748)
- Windows OS error 33 (#747)
- Embedding download robustness (#740)
* fix: bump intra-workspace dependency versions to 0.9.0
uteke-cli, uteke-mcp, and uteke-server had hardcoded uteke-core/mcp
version = "0.8.0" instead of using workspace version. Updated all
to 0.9.0 to match workspace.package.version.
* docs: add contributors section to v0.9.0 changelog
Thank @webhop123 for Windows OS error 33 fix (#747).
* fix(api): ensure leading slash on versioned API path after strip_prefix
ApiVersion::from_path() stripped '/api/v1/' prefix producing 'recall'
instead of '/recall', causing all /api/v1/* and /api/v2/* routes to
return 404. The fix ensures the stripped path always starts with '/'.
Also adds @gnoviawan to v0.9.0 CHANGELOG contributors.
* fix(api): ensure leading slash on versioned API path after strip_prefix
ApiVersion::from_path() stripped '/api/v1/' prefix producing 'recall'
instead of '/recall', causing all /api/v1/* and /api/v2/* routes to
return 404. The fix strips '/api/v1' (no trailing slash), keeping the
leading '/' in the remainder for correct route pattern matching.
Also adds @gnoviawan to v0.9.0 CHANGELOG contributors.
* chore: release v0.9.1 — patch fix for API versioning routes
- Fix ApiVersion::from_path() strip_prefix producing path without leading /
- Add @gnoviawan to CHANGELOG contributors
- Bump workspace + intra-workspace deps to 0.9.1
* chore: upgrade Rust edition 2021 → 2024 (#755)
- edition 2021 → 2024 (requires Rust 1.85+)
- rust-version 1.75 → 1.85 (edition 2024 MSRV)
Audit: no unsafe extern blocks, no gen keyword, no raw pointer derives.
Codebase uses explicit dyn, no edition-breaking patterns found.
Closes #755
* fix: edition 2024 — remove explicit ref bindings + apply rustfmt
Edition 2024 introduces implicitly-borrowing patterns, making
`Some(ref x)` an error. Also changes rustfmt import grouping.
- doc.rs:348: Some(ref json_str) → Some(json_str)
- room.rs:95: Some(ref q) → Some(q)
- cargo fmt --all (38 files, import reorder)
* fix: wrap set_var/remove_var in unsafe blocks (edition 2024)
Edition 2024 makes std::env::set_var and std::env::remove_var unsafe.
All test code using these functions now wraps calls in unsafe {} blocks.
- uteke-core/src/lib.rs: 5 set_var + 10 remove_var
- uteke-cli/src/config.rs: 14 set_var + 19 remove_var
* fix: clippy edition 2024 lints — repeat_n + is_none_or
- graph_rerank.rs: repeat(?).take(n) → repeat_n(?, n)
- lib.rs: .map_or(true, |_| true) → .is_none_or(|_| true)
* fix: clippy let_binding_from_block in slug_from_path (edition 2024)
Remove unnecessary let binding — return the expression directly.
* fix: expose room_remember via HTTP + forget returns 404 (#762)
Bug A: POST /remember ignored 'room' field because remember_in_room()
was never exposed via HTTP. Added POST /room/remember endpoint that
calls uteke.remember_in_room() — stores memory AND links to room.
Bug B: DELETE /forget returned 200 even when memory ID doesn't exist.
Now checks existence first and returns proper 404.
Tags field: verified code path is correct — deserialization and storage
both handle tags properly. The production 500 was an infrastructure
issue (embedder), not a code bug.
* chore: release v0.10.0 — edition 2024 + room/remember endpoint
- Rust edition 2021 → 2024, min Rust 1.85
- POST /room/remember HTTP endpoint
- DELETE /forget returns 404 for non-existent IDs
- README/docs Rust version badges updated
* feat: rewrite hermes-memory-provider as pre_llm_call plugin hook
* fix(test): update memory_provider tests for pre_llm_call plugin
* fix(plugin): remove stale @staticmethod, use context manager for file read
- @staticmethod on module-level function causes TypeError at runtime
- open() without context manager leaks file handle
Both found by CodeCora review.
* fix: uteke-tool template dynamic namespace and room ns params
* fix: skip DB open for upgrade command (#772), fix doc/delete query parsing (#776)
Fix #772: uteke upgrade hangs when database is locked
- Add Commands::Upgrade to early-exit block in main.rs (alongside
Completions, Init, Onboard, Bench). Upgrade only needs network +
filesystem — no store access required.
Fix #776: DELETE /doc/delete fails to parse query params
- doc_delete handler passed full URL to parse_query_param(), which
only splits on '&' and '=' without stripping the path portion.
- Use req.url().query() instead, which returns just the query string.
Matches the pattern already used in parse_query_namespace().
* fix CI: make upgrade module pub(crate), use split pattern for query string
- commands/mod.rs: mod upgrade → pub(crate) mod upgrade (was private,
main.rs couldn't access it)
- handlers.rs: req.url().query() → req.url().split('?').nth(1) because
tiny_http Request::url() returns &str, not url::Url
* feat: migrate default data dir ~/.uteke → ~/.codecora/uteke with auto-migration (#773)
- uteke_home() now defaults to ~/.codecora/uteke
- Auto-migrates legacy ~/.uteke on first run (rename + cross-device fallback)
- CLI global_config_path() uses uteke_core::uteke_home() instead of hardcoded path
- Updated all docs, CLI help text, and extension templates
- Test input strings for set_namespace_in_toml intentionally kept (simulates old user config)
- CHANGELOG.md historical entries untouched
* style: apply cargo fmt to lib.rs
* fix: remove redundant &format! borrow in Error::generic calls (clippy)
* fix: address review — Windows paths in INSTALL.md, sandbox test to temp dir
* fix: prevent env var test pollution between parallel test threads
Both uteke_home tests now save and restore HOME/UTEKE_HOME env vars
to prevent race conditions when Rust runs tests in parallel.
* style: use if-let instead of match for single-pattern clippy
* release: bump v0.10.1
* fix: isolate test_uteke_home_with_env from parallel env var pollution
Race condition: test_uteke_home_default_no_migration sets HOME and
removes UTEKE_HOME while test_uteke_home_with_env sets UTEKE_HOME.
When running in parallel, remove_var from one thread can clobber
set_var from another, causing the assertion to see the default path
instead of the UTEKE_HOME override.
Fix: also set HOME to a unique temp dir in the env test, matching
the pattern already used by the default-path test.
* docs: fix hermes integration examples and add API notes
* chore: bump quinn-proto 0.11.14 → 0.11.15 (GHSA-4w2j-m93h-cj5j)
Fixes Trivy HIGH vulnerability in transitive dependency chain:
reqwest → quinn → quinn-proto
Also syncs Cargo.lock with workspace version 0.10.1.
* chore(deps): bump which from 7.0.3 to 8.0.5
Bumps [which](https://github.com/harryfei/which-rs) from 7.0.3 to 8.0.5.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/harryfei/which-rs/compare/7.0.3...8.0.5)
---
updated-dependencies:
- dependency-name: which
dependency-version: 8.0.5
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
* fix: filter deprecated memories from room operations (#784, #785, #786) (#790)
* fix: filter deprecated memories from room operations (#784, #785, #786)
#784 — room_stats, recall_room, get_room_memory_ids now filter
deprecated=0 via INNER JOIN. Previously counted deprecated memories
causing 76% stat inflation (620 vs 148). Added 3 test cases.
#785 — POST /room/recall query is now optional. When query is
None/empty, falls back to chronological recall_room instead of
returning 400 error.
#786 — Added docs/api-reference.md with full HTTP API reference
covering 30+ endpoints. Added to VitePress sidebar.
Closes #784, #785, #786
* style: cargo fmt
---------
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* fix: runtime ORT dispatch for non-AVX2 CPUs + validation error mapping (#709, #789) (#792)
* feat(onnx): runtime ORT library dispatch for non-AVX2 CPUs (#709)
Switch from ort's download-binaries (which bundles AVX2-only lib) to
load-dynamic, enabling runtime selection between AVX2 and SSE4.2 ORT
shared libraries.
Changes:
- Cargo.toml: ort features = [load-dynamic, ndarray, api-18]
(api-18 works around pykeio/ort#547 vitis EP compile error)
- New embed/ort_init.rs: CPU feature detection + library resolution
(ORF_LIB_PATH env > ./libonnxruntime.so > ./ort-legacy/libonnxruntime.so)
- engine.rs: call init_ort_environment() before OnnxEmbedder::new()
- Non-ORT backends (custom, ollama, openai) are unaffected
CI packaging and legacy ORT sidecar build will follow in next commits.
* ci(release): add ORT sidecar packaging + legacy SSE4.2 build
Step 5 & 6 — CI pipeline and release packaging for load-dynamic ORT.
Release workflow changes:
- Add build-ort-legacy job: builds ORT 1.24.4 from source with
onnxruntime_USE_AVX=OFF (SSE4.2 only) for SIGILL fix (#709)
- Add package-legacy job: assembles legacy Linux x64 bundle with
ort-legacy/ sidecar alongside standard ORT libraries
- All platforms now download prebuilt ORT shared libs from GitHub
releases (load-dynamic requires runtime .so/.dylib/.dll)
- Copy symlinks + actual .so files for proper dlopen resolution
- Docker build: arch-specific ort-amd64/ort-arm64 subfolders to
avoid cross-arch .so contamination in multi-platform builds
Dockerfile changes:
- Copy ORT shared libs to /usr/local/lib/ with ldconfig
- Select arch-specific ORT libs via TARGETARCH
- Copies libonnxruntime_providers_shared.so too
ort_init.rs changes:
- Add system lib path fallback (/usr/local/lib, /usr/lib, /lib)
for Docker containers where binary and .so are in different dirs
- Update module doc with full 5-step resolution order
Refs: #709
* fix: return 400 for Validation errors in /room/remember (#789)
* ci(release): add Windows legacy SSE4.2 ORT build (#709)
- Add build-ort-legacy-windows job: builds ORT from source on windows-latest
with AVX/AVX2/AVX512 explicitly disabled (SSE4.2 only)
- Add package-legacy-windows job: combines standard bundle + legacy DLLs
- Update release job to depend on package-legacy-windows
- Add Windows legacy row to release notes table
- Guard Unix-only system lib paths with #[cfg(unix)] in ort_init.rs
* docs: update CHANGELOG for #709 #785 #789
* style: apply cargo fmt to engine.rs
---------
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* chore(deps): bump uuid from 1.23.5 to 1.24.0
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.23.5 to 1.24.0.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/v1.23.5...v1.24.0)
---
updated-dependencies:
- dependency-name: uuid
dependency-version: 1.24.0
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
* feat: auto-generated API reference docs via schemars (#786) (#795)
Add crates/docgen — a standalone binary that reads the route registry
and type schemas to generate docs/api-reference.md automatically.
Architecture:
- api_registry.rs: single source of truth for endpoint metadata
(method, path, description, request/response types, related issues)
- types.rs: #[derive(JsonSchema)] behind docgen feature flag
- docgen binary: reads registry + generates JSON schemas via schemars,
renders to structured markdown with field tables
- CI: docs-check job fails if api-reference.md is stale
The docgen feature flag ensures zero runtime overhead — schemars is
only compiled when generating docs, not in production builds.
Closes #786
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* chore(deps): bump thiserror from 2.0.18 to 2.0.19
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 2.0.18 to 2.0.19.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/2.0.18...2.0.19)
---
updated-dependencies:
- dependency-name: thiserror
dependency-version: 2.0.19
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
* chore(deps): bump serde_json from 1.0.150 to 1.0.151
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.150 to 1.0.151.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.150...v1.0.151)
---
updated-dependencies:
- dependency-name: serde_json
dependency-version: 1.0.151
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
* fix: accept short ID prefix in DELETE /forget (#794) (#796)
list and room_recall display only 8-char UUID prefixes, but forget
required a full UUID — making it impossible to delete memories from
CLI output.
- Add resolve_id_prefix() to crud + operations (LIKE query, returns
full ID for exact match, Err for ambiguous)
- Handler tries full UUID first, then resolves prefix on failure
- Ambiguous prefix (>1 match) returns 400 with count
- Still returns 404 if prefix doesn't match any memory
Closes #794
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* chore: v0.10.2 pre-release — version bump + docs sync (#797)
- Bump workspace version 0.10.1 → 0.10.2
- CHANGELOG: promote [Unreleased] → [0.10.2], add entries for
#784 (deprecated memory filter), #785 (optional room/recall query),
#794 (short ID prefix in forget), auto-generated API docs (#786),
quinn-proto security patch
- README.md: v0.7.3 → v0.10.2, test count 206 → 431
- README.id.md: v0.7.2 → v0.10.2, test count 206 → 431,
~/.uteke → ~/.codecora/uteke
- INSTALL.md: version examples 0.0.2/0.0.7 → 0.10.2
- Regenerate docs/api-reference.md (version bump)
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* chore(deps): bump clap from 4.6.1 to 4.6.4
Bumps [clap](https://github.com/clap-rs/clap) from 4.6.1 to 4.6.4.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.6.1...clap_complete-v4.6.4)
---
updated-dependencies:
- dependency-name: clap
dependency-version: 4.6.3
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
* Release v0.10.2 (#798)
* docs: fix hermes integration examples and add API notes
* chore: bump quinn-proto 0.11.14 → 0.11.15 (GHSA-4w2j-m93h-cj5j)
Fixes Trivy HIGH vulnerability in transitive dependency chain:
reqwest → quinn → quinn-proto
Also syncs Cargo.lock with workspace version 0.10.1.
* fix: filter deprecated memories from room operations (#784, #785, #786) (#790)
* fix: filter deprecated memories from room operations (#784, #785, #786)
#784 — room_stats, recall_room, get_room_memory_ids now filter
deprecated=0 via INNER JOIN. Previously counted deprecated memories
causing 76% stat inflation (620 vs 148). Added 3 test cases.
#785 — POST /room/recall query is now optional. When query is
None/empty, falls back to chronological recall_room instead of
returning 400 error.
#786 — Added docs/api-reference.md with full HTTP API reference
covering 30+ endpoints. Added to VitePress sidebar.
Closes #784, #785, #786
* style: cargo fmt
---------
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* fix: runtime ORT dispatch for non-AVX2 CPUs + validation error mapping (#709, #789) (#792)
* feat(onnx): runtime ORT library dispatch for non-AVX2 CPUs (#709)
Switch from ort's download-binaries (which bundles AVX2-only lib) to
load-dynamic, enabling runtime selection between AVX2 and SSE4.2 ORT
shared libraries.
Changes:
- Cargo.toml: ort features = [load-dynamic, ndarray, api-18]
(api-18 works around pykeio/ort#547 vitis EP compile error)
- New embed/ort_init.rs: CPU feature detection + library resolution
(ORF_LIB_PATH env > ./libonnxruntime.so > ./ort-legacy/libonnxruntime.so)
- engine.rs: call init_ort_environment() before OnnxEmbedder::new()
- Non-ORT backends (custom, ollama, openai) are unaffected
CI packaging and legacy ORT sidecar build will follow in next commits.
* ci(release): add ORT sidecar packaging + legacy SSE4.2 build
Step 5 & 6 — CI pipeline and release packaging for load-dynamic ORT.
Release workflow changes:
- Add build-ort-legacy job: builds ORT 1.24.4 from source with
onnxruntime_USE_AVX=OFF (SSE4.2 only) for SIGILL fix (#709)
- Add package-legacy job: assembles legacy Linux x64 bundle with
ort-legacy/ sidecar alongside standard ORT libraries
- All platforms now download prebuilt ORT shared libs from GitHub
releases (load-dynamic requires runtime .so/.dylib/.dll)
- Copy symlinks + actual .so files for proper dlopen resolution
- Docker build: arch-specific ort-amd64/ort-arm64 subfolders to
avoid cross-arch .so contamination in multi-platform builds
Dockerfile changes:
- Copy ORT shared libs to /usr/local/lib/ with ldconfig
- Select arch-specific ORT libs via TARGETARCH
- Copies libonnxruntime_providers_shared.so too
ort_init.rs changes:
- Add system lib path fallback (/usr/local/lib, /usr/lib, /lib)
for Docker containers where binary and .so are in different dirs
- Update module doc with full 5-step resolution order
Refs: #709
* fix: return 400 for Validation errors in /room/remember (#789)
* ci(release): add Windows legacy SSE4.2 ORT build (#709)
- Add build-ort-legacy-windows job: builds ORT from source on windows-latest
with AVX/AVX2/AVX512 explicitly disabled (SSE4.2 only)
- Add package-legacy-windows job: combines standard bundle + legacy DLLs
- Update release job to depend on package-legacy-windows
- Add Windows legacy row to release notes table
- Guard Unix-only system lib paths with #[cfg(unix)] in ort_init.rs
* docs: update CHANGELOG for #709 #785 #789
* style: apply cargo fmt to engine.rs
---------
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* feat: auto-generated API reference docs via schemars (#786) (#795)
Add crates/docgen — a standalone binary that reads the route registry
and type schemas to generate docs/api-reference.md automatically.
Architecture:
- api_registry.rs: single source of truth for endpoint metadata
(method, path, description, request/response types, related issues)
- types.rs: #[derive(JsonSchema)] behind docgen feature flag
- docgen binary: reads registry + generates JSON schemas via schemars,
renders to structured markdown with field tables
- CI: docs-check job fails if api-reference.md is stale
The docgen feature flag ensures zero runtime overhead — schemars is
only compiled when generating docs, not in production builds.
Closes #786
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* fix: accept short ID prefix in DELETE /forget (#794) (#796)
list and room_recall display only 8-char UUID prefixes, but forget
required a full UUID — making it impossible to delete memories from
CLI output.
- Add resolve_id_prefix() to crud + operations (LIKE query, returns
full ID for exact match, Err for ambiguous)
- Handler tries full UUID first, then resolves prefix on failure
- Ambiguous prefix (>1 match) returns 400 with count
- Still returns 404 if prefix doesn't match any memory
Closes #794
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* chore: v0.10.2 pre-release — version bump + docs sync (#797)
- Bump workspace version 0.10.1 → 0.10.2
- CHANGELOG: promote [Unreleased] → [0.10.2], add entries for
#784 (deprecated memory filter), #785 (optional room/recall query),
#794 (short ID prefix in forget), auto-generated API docs (#786),
quinn-proto security patch
- README.md: v0.7.3 → v0.10.2, test count 206 → 431
- README.id.md: v0.7.2 → v0.10.2, test count 206 → 431,
~/.uteke → ~/.codecora/uteke
- INSTALL.md: version examples 0.0.2/0.0.7 → 0.10.2
- Regenerate docs/api-reference.md (version bump)
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
---------
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* fix(ci): Windows legacy ORT C4875 suppress + Docker artifact download (#799)
- Add /wd4875 to Windows legacy ORT CMake to suppress C4875
(non-string literal [[gsl::suppress]] deprecated) treated as error
- Remove merge-multiple from Docker artifact download — v7 auto-extracts
which loses the .tar.gz wrapper the script expects
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* fix(ci): Windows legacy ORT C4875 suppress + Docker artifact download (#801)
- Add /wd4875 to Windows ORT cmake (C4875 gsl::suppress deprecated)
- Remove merge-multiple from Docker artifact download (v7 auto-extracts)
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* fix(ci): Windows ORT CMP0091 + Docker/Windows skip-decompress (#802)
* docs: fix hermes integration examples and add API notes
* chore: bump quinn-proto 0.11.14 → 0.11.15 (GHSA-4w2j-m93h-cj5j)
Fixes Trivy HIGH vulnerability in transitive dependency chain:
reqwest → quinn → quinn-proto
Also syncs Cargo.lock with workspace version 0.10.1.
* fix: filter deprecated memories from room operations (#784, #785, #786) (#790)
* fix: filter deprecated memories from room operations (#784, #785, #786)
#784 — room_stats, recall_room, get_room_memory_ids now filter
deprecated=0 via INNER JOIN. Previously counted deprecated memories
causing 76% stat inflation (620 vs 148). Added 3 test cases.
#785 — POST /room/recall query is now optional. When query is
None/empty, falls back to chronological recall_room instead of
returning 400 error.
#786 — Added docs/api-reference.md with full HTTP API reference
covering 30+ endpoints. Added to VitePress sidebar.
Closes #784, #785, #786
* style: cargo fmt
---------
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* fix: runtime ORT dispatch for non-AVX2 CPUs + validation error mapping (#709, #789) (#792)
* feat(onnx): runtime ORT library dispatch for non-AVX2 CPUs (#709)
Switch from ort's download-binaries (which bundles AVX2-only lib) to
load-dynamic, enabling runtime selection between AVX2 and SSE4.2 ORT
shared libraries.
Changes:
- Cargo.toml: ort features = [load-dynamic, ndarray, api-18]
(api-18 works around pykeio/ort#547 vitis EP compile error)
- New embed/ort_init.rs: CPU feature detection + library resolution
(ORF_LIB_PATH env > ./libonnxruntime.so > ./ort-legacy/libonnxruntime.so)
- engine.rs: call init_ort_environment() before OnnxEmbedder::new()
- Non-ORT backends (custom, ollama, openai) are unaffected
CI packaging and legacy ORT sidecar build will follow in next commits.
* ci(release): add ORT sidecar packaging + legacy SSE4.2 build
Step 5 & 6 — CI pipeline and release packaging for load-dynamic ORT.
Release workflow changes:
- Add build-ort-legacy job: builds ORT 1.24.4 from source with
onnxruntime_USE_AVX=OFF (SSE4.2 only) for SIGILL fix (#709)
- Add package-legacy job: assembles legacy Linux x64 bundle with
ort-legacy/ sidecar alongside standard ORT libraries
- All platforms now download prebuilt ORT shared libs from GitHub
releases (load-dynamic requires runtime .so/.dylib/.dll)
- Copy symlinks + actual .so files for proper dlopen resolution
- Docker build: arch-specific ort-amd64/ort-arm64 subfolders to
avoid cross-arch .so contamination in multi-platform builds
Dockerfile changes:
- Copy ORT shared libs to /usr/local/lib/ with ldconfig
- Select arch-specific ORT libs via TARGETARCH
- Copies libonnxruntime_providers_shared.so too
ort_init.rs changes:
- Add system lib path fallback (/usr/local/lib, /usr/lib, /lib)
for Docker containers where binary and .so are in different dirs
- Update module doc with full 5-step resolution order
Refs: #709
* fix: return 400 for Validation errors in /room/remember (#789)
* ci(release): add Windows legacy SSE4.2 ORT build (#709)
- Add build-ort-legacy-windows job: builds ORT from source on windows-latest
with AVX/AVX2/AVX512 explicitly disabled (SSE4.2 only)
- Add package-legacy-windows job: combines standard bundle + legacy DLLs
- Update release job to depend on package-legacy-windows
- Add Windows legacy row to release notes table
- Guard Unix-only system lib paths with #[cfg(unix)] in ort_init.rs
* docs: update CHANGELOG for #709 #785 #789
* style: apply cargo fmt to engine.rs
---------
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* feat: auto-generated API reference docs via schemars (#786) (#795)
Add crates/docgen — a standalone binary that reads the route registry
and type schemas to generate docs/api-reference.md automatically.
Architecture:
- api_registry.rs: single source of truth for endpoint metadata
(method, path, description, request/response types, related issues)
- types.rs: #[derive(JsonSchema)] behind docgen feature flag
- docgen binary: reads registry + generates JSON schemas via schemars,
renders to structured markdown with field tables
- CI: docs-check job fails if api-reference.md is stale
The docgen feature flag ensures zero runtime overhead — schemars is
only compiled when generating docs, not in production builds.
Closes #786
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* fix: accept short ID prefix in DELETE /forget (#794) (#796)
list and room_recall display only 8-char UUID prefixes, but forget
required a full UUID — making it impossible to delete memories from
CLI output.
- Add resolve_id_prefix() to crud + operations (LIKE query, returns
full ID for exact match, Err for ambiguous)
- Handler tries full UUID first, then resolves prefix on failure
- Ambiguous prefix (>1 match) returns 400 with count
- Still returns 404 if prefix doesn't match any memory
Closes #794
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* chore: v0.10.2 pre-release — version bump + docs sync (#797)
- Bump workspace version 0.10.1 → 0.10.2
- CHANGELOG: promote [Unreleased] → [0.10.2], add entries for
#784 (deprecated memory filter), #785 (optional room/recall query),
#794 (short ID prefix in forget), auto-generated API docs (#786),
quinn-proto security patch
- README.md: v0.7.3 → v0.10.2, test count 206 → 431
- README.id.md: v0.7.2 → v0.10.2, test count 206 → 431,
~/.uteke → ~/.codecora/uteke
- INSTALL.md: version examples 0.0.2/0.0.7 → 0.10.2
- Regenerate docs/api-reference.md (version bump)
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* fix(ci): Windows legacy ORT C4875 suppress + Docker artifact download (#799)
- Add /wd4875 to Windows legacy ORT CMake to suppress C4875
(non-string literal [[gsl::suppress]] deprecated) treated as error
- Remove merge-multiple from Docker artifact download — v7 auto-extracts
which loses the .tar.gz wrapper the script expects
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* fix(ci): Windows ORT CMP0091 policy + Docker/Windows skip-decompress
- Add CMAKE_POLICY_DEFAULT_CMP0091=NEW to ORT build so CMAKE_MSVC_RUNTIME_LIBRARY
propagates to FetchContent deps (protobuf). Fixes LNK2038 RuntimeLibrary mismatch.
- Add skip-decompress: true to Docker download-artifact to preserve .tar.gz files
- Add skip-decompress: true to Windows legacy packaging to preserve .zip artifact
---------
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* fix(ci): remove Windows legacy ORT build + upgrade download-artifact to v8 (#803)
- Remove build-ort-legacy-windows job (ORT v1.24.4 removed
--enable_msvc_static_runtime, internal protobuf hardcodes /MT
causing unfixable LNK2038)
- Remove package-legacy-windows job (depends on removed ORT build)
- Upgrade download-artifact v4→v8 in Docker job with skip-decompress:
true (v4 doesn't support skip-decompress, was silently ignored)
- Update release notes: remove Windows legacy bundle reference
- Linux legacy bundle preserved (no CRT mismatch on Linux)
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* fix(ci): simplify Docker artifact extraction - v4 default auto-extract (#804)
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* fix(ci): create binaries dir inside release-artifacts after cd (#806)
The mkdir -p binaries ran before cd release-artifacts, creating the
directory in the workspace root. Subsequent mv commands wrote to
release-artifacts/binaries/ instead, leaving the Docker context
binaries/ empty. Fix: cd first, then mkdir.
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* fix(ci): keep binaries/ at workspace root for Docker context (#808)
Dockerfile does 'COPY binaries/ ./' expecting binaries/ at the build
context root (workspace root). The previous fix (#806) moved 'cd
release-artifacts' before 'mkdir binaries', which placed binaries/
inside release-artifacts/ — invisible to Docker build context.
Root cause: download-artifact@v4 downloads into release-artifacts/,
but Docker context is workspace root (.). Fix: don't cd. Create
binaries/ at workspace root, find artifacts via 'find release-artifacts'.
Fixes the 'COPY binaries/ not found' Docker buildx error.
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* fix(ort): force /MD runtime for Windows legacy ORT build
ONNX Runtime v1.24.4 build from source on Windows fails with LNK2038
RuntimeLibrary mismatch: protobuf (MT_StaticRelease) vs onnxruntime
(MD_DynamicRelease). Add -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL
to force dynamic CRT across all dependencies.
Bump cache key v1→v2 to invalidate cached build without the flag.
* fix(ort): force /MD + /NODEFAULTLIB for Windows ORT build
CMAKE_MSVC_RUNTIME_LIBRARY alone insufficient — protobuf submodule
ignores it and compiles with /MT. Add explicit /MD to CMAKE_CXX_FLAGS
and CMAKE_C_FLAGS, plus /NODEFAULTLIB:LIBCMT;LIBCPMT to linker to
prevent static CRT symbols from being linked.
Bump cache key v2→v3.
* ci: drop Windows legacy ORT build and package jobs
Windows ORT build from source fails with CRT mismatch (LNK2038) —
protobuf submodule hardcodes /MT regardless of CMAKE_MSVC_RUNTIME_LIBRARY.
Removes:
- build-ort-legacy-windows job
- package-legacy-windows job
- package-legacy-windows from release needs
- Windows legacy bundle from release notes
99%+ Windows CPUs (Intel Haswell 2013+, AMD Excavator 2015+) support
AVX2. Pre-built AVX2 release already works. Linux legacy bundle
retained for server-grade old hardware (Celeron J4125/N4020).
* ci: filter release artifacts to avoid Docker buildx metadata
Download step used merge-multiple:true without pattern, downloading
ALL artifacts including Docker buildx metadata (.dockerbuild zip).
This corrupt artifact caused "download failed after 5 retries".
Fix: add pattern: uteke-* to only download release binary artifacts.
* ci: fix release notes template — heredoc with proper YAML escaping
* chore(deps): bump actions/cache from 4 to 6
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 6.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v6)
---
updated-dependencies:
- dependency-name: actions/cache
dependency-version: '6'
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
* fix: ORT init cascade — AVX2 fallthrough, error message accuracy, embedder init cache
Fixes #820, #821, #822
#820 — ort_init.rs: AVX2 path no longer early-returns Err when standard
library is not found in exe_dir. Falls through to system paths
(/usr/local/lib, /usr/lib, /lib) which is the common Docker layout.
Added !avx2 guard on legacy path to prevent AVX2 CPUs from accidentally
loading SSE4.2-only library. Descriptive error at the end lists all
searched locations.
#821 — engine.rs: Replaced misleading 'CPU may lack required SIMD support'
error message with actionable 'Set ORT_LIB_PATH to the library file,
or use the standard release bundle.' The old message sent users down
the wrong debugging path (checking CPU capabilities instead of library
placement).
#822 — lib.rs: Added embedder_init_error: Mutex<Option<String>> field
that caches the first embedder initialization failure. Subsequent calls
to ensure_embedder() return immediately with the cached error instead
of retrying the expensive ORT/model init on every request. This prevents
log spam and CPU waste in Docker environments where the ORT library is
persistently missing. Uses String storage because Error type is not Clone.
Tests: 379 passed, 0 failed.
* fix: TTL-based error cache for transient network failures (#822 review)
- Change embedder_init_error from Mutex<Option<String>> to
Mutex<Option<(String, Instant, bool)>> with permanent/transient flag
- ONNX failures (missing lib) cached permanently — no point retrying
- OpenAI/Ollama failures use 60s TTL — auto-retry after outage passes
- Address Cora code review: prevent permanent disable on transient errors
- Fix rustfmt string continuation in non-onnx error message
Fixes CodeCora review feedback on #822.
* release: v0.10.3 — ORT init fixes, dep bumps
* revert: undo PR #837 merge (develop → main)
Reverts all changes merged via PR #837 (develop → main).
Main restored to pre-merge state (8a12376).
Reason: PR was merged with --admin bypass on failing CI
(Branch Naming check failed for develop branch).
Must fix pr-checks.yml before re-merging.
* chore(release): v0.11.0 — code + docs changes from develop
Re-applies all v0.11.0 content that was reverted by PR #838.
Includes: version bump, changelog, code fixes (doc_move, recall score,
governance docs), issue templates, API reference.
* chore: merge develop to main for v0.14.0 release (#1020)
* fix(docs): standardize recall latency to ~45ms
3 files still referenced ~30ms (stale from early benchmarks).
Actual measured P50 at 1K-10K memories is ~45ms per benchmarks.md.
Updated: architecture.md, mcp.md, index.md
* chore: harden .gitignore + restructure repo for public safety
- Add internal/ to .gitignore (prevents future strategy/launch doc leaks)
- Add benchmarks/longmemeval/results_*/ to .gitignore (ephemeral data)
- Add docs/package-lock.json to .gitignore (VitePress build artifact)
- Move blog/comparison-2026.md → docs/comparison-2026.md (consolidate docs)
- Remove stale benchmarks/longmemeval/results_diverse/ (raw output, not summary)
- Remove docs/package-lock.json from tracking
Audit confirms: zero internal/ files in entire git history after filter-repo scrub.
* feat: hybrid as default recall strategy + import fixes (#1005, #1014) (#1015)
* feat(bench): batch import + strategy flag + API embedding auto-config
- Replace 53x individual remember calls with single JSONL batch import (10x speedup)
- Add --strategy flag to pass vector|hybrid to uteke recall
- Auto-configure embedding API from EMBED_API_KEY/EMBED_API_BASE env vars
- Add --chunk-sessions flag for session chunking (Tier 2 prep)
- Dedup recall results by session_id (first occurrence = highest rank)
- Raise subprocess timeout 120s -> 600s for large imports
Initial 5Q results: hybrid R@5=1.000 vs vector R@5=0.800 (+20pp)
* fix(bench): only mark sessions inserted after import succeeds
Address Cora findings:
- Move inserted_sids population to after batch import success
- Parse import response to verify imported_count > 0
- Log warning if import returns 0 inserted
* fix(bench): resolve uteke binary by absolute path to avoid x86_64 PATH clash
Background subprocess calls were resolving to /opt/data/.cargo/bin/uteke
(x86_64) instead of target/release/uteke (AArch64). Now resolves relative
to repo root with shutil.which fallback.
* docs(bench): add 50Q hybrid results — R@5 98.0%, R@10 100%
Strategy comparison (uteke only):
- Vector 500Q: R@5=85.4%, R@10=88.5%, NDCG@5=0.810
- Hybrid 50Q: R@5=98.0%, R@10=100%, NDCG@5=0.960
- Improvement: +12.6pp R@5
Hybrid uses RRF (k=60) fusion of vector + FTS5 search.
1 miss at R@5 (single-session-user), recovered at R@10.
run_eval.py changes:
- Add --strategy flag (vector|hybrid)
- Throttle: taskset -c 0-1 + nice -n 19
- Absolute binary path resolution
- Timeout: 900s per question
* fix(core): add dedup, retry, and auto_link to import path (#1005)
Import pipeline was missing 3 features that remember() has:
1. Dedup check — cosine >= 0.95 skips duplicate entries
2. Retry on embedding failure — 3 retries with backoff
3. Auto-link cosine edges — graph edges for imported memories
Changes:
- import_export.rs: call check_duplicate() before insert, use
retry_embed() instead of single-attempt embed(), call
auto_link_cosine() after successful insert
- operations.rs: make retry_embed() and check_duplicate() pub(crate)
so they're accessible from import_export.rs
Import path is now semantically consistent with remember() path.
* fix(core): cross-compile ORT_LIB_NAME for Android/iOS (#1014)
Add target_os = "android" to Linux .so branch and target_os = "ios"
to macOS .dylib branch. Without these, uteke-core fails to compile
for mobile targets with E0425 (cannot find value ORT_LIB_NAME).
Android uses libonnxruntime.so (same as Linux, loaded via jniLibs).
iOS uses libonnxruntime.dylib (same as macOS, framework embedded).
This unblocks uteke-mobile cross-compilation.
* feat(core): hybrid as default recall strategy
Change default_strategy from vector to hybrid (RRF: vector + FTS5).
Benchmark justification (LongMemEval-S):
- Vector 500Q: R@5=85.4%, R@10=88.5%
- Hybrid 50Q: R@5=98.0%, R@10=100.0%
- Improvement: +12.6pp R@5
Changes:
- config.rs: default_strategy = hybrid + assert in test
- cli.rs: update help text default to hybrid
- recall.rs: update comment to reflect new default
- configuration.md: update all references
- cli-reference.md: reorder examples, hybrid first as default
Users who want vector-only: set default_strategy = vector in config
or use --strategy vector flag.
* fix(bench): guard taskset/nice with sys.platform check
Cora finding: taskset is Linux-only, would FileNotFoundError on macOS.
Now conditionally applied only when sys.platform == 'linux'.
---------
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* perf: Phase 2 — skip update check in batch mode, pre-truncate embedding text (#1006, #1002) (#1016)
#1006: CLI update check skipped when UTEKE_NO_UPDATE_CHECK env var is set.
Benchmark script sets this automatically, saving ~500ms per subprocess call.
#1002: Pre-truncate text before tokenizer to avoid wasted CPU on tokens
beyond MAX_SEQ_LEN (2048). Uses 4 chars/token heuristic with char-boundary
safety.
#1003: Already resolved — idx_memories_namespace index exists in SCHEMA_INDEXES.
#1004: Already resolved — compute_graph_signals uses single batched SQL query.
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
* feat: Phase 3 — lifecycle/deprecated, memory tools guide, source provenance (#1007, #1010, #1012, #1013) (#1017)
* feat: Phase 3 — lifecycle/deprecated endpoint, memory tools guide, source provenance (#1007, #1010, #1012, #1013)
#1007: GET /lifecycle/deprecated — list deprecated …
- Fix contradiction: 'No CLA required' vs the active cla-check.yml bot that blocks unsigned PRs. Contributors reading CONTRIBUTING.md were told no CLA was needed, then hit the bot on their first PR. - Add explicit CLA section with sign links (individual + corporate). - Add 'Contributions are unpaid' section: voluntary, no compensation, no bounty — volunteer work under Apache-2.0. Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…file (#1164) - Bump optional vecq-core dep to 0.3 (issue #1161). API surface used by the integration (new/add/search/len/dim/to_bytes/from_bytes) is unchanged; 0.3.0 reads all persisted formats v1-v1.5. - Switch create_index() to VecqIndex::with_residual: 4-bit base quantization + residual refinement. with_residual pins bits=4 (residual requires the 4-bit width), so the storage profile matches the legacy 0.1 build while recall improves on noise-dominated data (~2x scan cost). Avoids the new 5-bit default deliberately — the recommended profile from #1161 is 4-bit + residual. - File-format note: indexes written by 0.3.0 are not readable by older uteke builds (forward lock-in). Safe because SQLite remains the source of truth and 'uteke repair' rebuilds the index from stored embeddings. Verified: - cargo test -p uteke-core --no-default-features --features onnx,vecq --lib: 529 passed, 0 failed - cargo test -p uteke-core --lib (usearch default): 528 passed - cargo build -p uteke-cli -p uteke-server -p uteke-mcp --no-default-features --features vecq: green Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…nd constructor (#1167) * fix(core): make open() embedder default feature-aware; add open_with_backend (#1166) - open() no longer hardcodes the 'onnx' embedder backend. On builds compiled without the onnx feature (e.g. pure vecq mobile profile) the default is now '' (no embedder configured), so the open path no longer fails with a misleading 'onnx disabled' error. - Add Uteke::open_with_backend(path, Option<&str>): explicit backend selection; None opens the store without an embedder for hosts that inject embeddings externally (FFI/mobile pipelines). - Dimension resolution ladder when no embedder is configured: UTEKE_EMBEDDING_DIMS > inferred from persisted embeddings > canonical 768 (DEFAULT_EMBEDDING_DIMS) with a warning. Previously dims 0 reached VecqIndex::new and panicked ('working_dim must be in 1..=0'). - remember() with no embedder stores the row FTS5-only (skips the vector index insert) instead of failing; vector backfill remains possible via uteke repair. - Store::infer_embedding_dims() reads the first persisted embedding length (length(blob)/4). - Tests: open/open_with_backend feature-aware matrix, FTS5 write+search without embedder (serial, UTEKE_EMBEDDING_DIMS), invalid-backend rejection, custom-backend behavior on non-onnx builds. Verified: - vecq-only (--no-default-features --features vecq): 526 passed, 0 failed - default (onnx+usearch): 531 passed, 0 failed - cli/server/mcp vecq build green, fmt clean * fix(core): skip cosine dedup + auto_link on empty embedding (#1166) Cora review follow-up: remember_embed passed the empty (no-embedder) embedding into check_duplicate, and remember_precomputed passed it to auto_link_cosine. Both are cosine-based and meaningless without a vector — guard them on non-empty embeddings. vecq-only: 526 passed; default: 531 passed. --------- Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…uild) (#1169) * feat(core): runtime-selectable vector engine — build usearch+vecq together, select via ENV/config (#1168) Replace the compile-time mutual exclusion of vector engines with runtime selection while keeping slim single-engine builds working. - vector.rs: unified runtime enum Engine { Usearch(Box<Index>), Vecq(VecqIndex) } behind the unchanged VectorIndex API; all cfg- branched sites became match arms on the enum. The vecq profile stays 4-bit + residual (with_residual, #1164) on every path. - compile_error guard: 'both engines' no longer errors — only 'no engine at all' does. Features are additive: desktop builds can compile both engines; mobile keeps single-engine builds (unused variants are eliminated at link time). - Selection precedence: UTEKE_VECTOR_BACKEND env > [vector] backend in uteke.toml > compiled-in default (usearch when present). Unknown values / engines not compiled in fall back to the default with a warning — open() never fails on a bad preference. - Per-engine index files kept (uteke_index.usearch / .vecq). Switching engines leaves the old file intact; the new engine starts empty and finish_open_full auto-rebuilds it from SQLite (source of truth). Both files coexist; switching back picks the old file up without a rebuild. - New API surface: VectorBackend (parse/resolve/default_backend), VectorIndex::with_backend / load_or_create_for / load_from_file_for, VectorIndex::backend(). - CLI: [vector] backend config + UTEKE_VECTOR_BACKEND env override. - Docs: configuration.md env table entry. Verified (all green; clippy 0 warnings and fmt clean on all three feature sets): - dual-engine (onnx+usearch+vecq): 537 passed - vecq-only slim (onnx+vecq): 535 passed - default (onnx+usearch): 534 passed; cli config tests: 45 passed - e2e: engine switch usearch->vecq->usearch on one store — auto-rebuild from SQLite, per-engine files coexist, switch back reuses the old file; invalid env falls back to default * ci: extend vecq-check with dual-engine tests (#1168) Guard the runtime-selection feature: dual-engine build must compile and pass the engine-switch + vector module tests on every PR. --------- Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…ecq (#1171) * feat(build): dual-engine default — official binaries ship usearch + vecq (#1168) Flip the default feature set of uteke-core and all forwarding crates (cli/server/mcp) to compile BOTH vector engines, making runtime selection (UTEKE_VECTOR_BACKEND / [vector] backend, #1169) available out of the box in official binaries and the Docker image. - default = [onnx, usearch, vecq] across the workspace. - Slim builds unchanged: --no-default-features --features vecq (mobile, pure Rust) or --features usearch (classic single-engine). - docker-compose: expose UTEKE_VECTOR_BACKEND (default usearch). - docs/docker.md: engine-choice section + volume layout (.usearch + .vecq coexist; switching rebuilds from SQLite, data untouched). Release binary size: 11.86 MB (was ~10.9 MB usearch-only; +~1 MB for the vecq engine). Verified: - cargo test -p uteke-core --lib (dual-engine default): 537 passed - cli config tests: 45 passed - Release-binary smoke test: default writes .usearch; switch to vecq rebuilds (hybrid recall finds the probe, score 1.13); switch back reuses the old .usearch; invalid env falls back cleanly (exit 0) * fix(core): drop unused VectorBackend import in engine-switch test CI clippy (-D warnings, workspace all-targets) flagged the import as unused — the test never names the type directly. --------- Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…bers (#1174) - RESULTS.md: new Independent Reproduction section (108Q subset re-run on ARM vs published Modal x86 run: 107/108 identical per-question rankings, 1 adjacent-rank near-tie explained, reproduction command) - README benchmark details: headline recall_any@5 98.2% + strict family, comparison chart embed, reproducibility callout - docs/benchmarks.md: Reproducibility subsection linking to RESULTS.md Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.1.9 to 1.1.10. - [Release notes](https://github.com/rust-lang/flate2-rs/releases) - [Commits](rust-lang/flate2-rs@1.1.9...1.1.10) --- updated-dependencies: - dependency-name: flate2 dependency-version: 1.1.10 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [which](https://github.com/harryfei/which-rs) from 8.0.5 to 8.0.6. - [Release notes](https://github.com/harryfei/which-rs/releases) - [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md) - [Commits](harryfei/which-rs@8.0.5...8.0.6) --- updated-dependencies: - dependency-name: which dependency-version: 8.0.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [usearch](https://github.com/unum-cloud/USearch) from 2.26.0 to 2.26.1. - [Release notes](https://github.com/unum-cloud/USearch/releases) - [Commits](unum-cloud/USearch@v2.26.0...v2.26.1) --- updated-dependencies: - dependency-name: usearch dependency-version: 2.26.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.24.0 to 1.26.0. - [Release notes](https://github.com/uuid-rs/uuid/releases) - [Commits](uuid-rs/uuid@v1.24.0...v1.26.0) --- updated-dependencies: - dependency-name: uuid dependency-version: 1.26.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…nlinked memories (#1173) (#1179) Incident (2026-08-31, room codecora-product): MCP room_delete wiped all room memory links while the tool replied 'memories preserved' — and the HTTP registry description claimed 'delete a room and all its memories'. Both messages were wrong in opposite directions; from an agent's point of view the room-scoped data became unreachable (memories survive in the store but lose their only room access path). Core delete_room() is and was unlink-only (ON DELETE CASCADE removes room_memories/room_documents links only). This change makes every surface tell the truth: - store delete_room() now returns the number of removed memory links - HTTP DELETE /room/delete responds { deleted, unlinked_memories, note } - MCP uteke_room_delete reports the count instead of a fixed claim - CLI room delete prints the count; --json includes unlinked_memories - api_registry description fixed + docs/api-reference.md regenerated Semantics stay unlink-only (consistent with CLI behavior all along); no destructive purge path is added in this PR. Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…ts (#1180) (#1182) POST /graph/edge validated source/target as memory IDs but inserted them directly into graph_edges, whose FKs reference graph_nodes(id) — every valid call violated the FK and returned 500. Both sides are now resolved to their linked graph node (existing graph node IDs stay accepted), or a node is ensured automatically before insertion. DELETE /graph/edge gains the same dual-ID resolution. POST response now carries source_node and target_node so clients can track the created nodes. Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…ategy (#1181) (#1183) * feat(api): namespace management — move, rename/merge, delete with strategy (#1181) Namespaces are a derived view over the memories.namespace column, but there was no sanctioned path to fix mistakes: no move, rename, or delete operation existed anywhere, and deprecated-only ghost namespaces looked identical to active ones in listings. - PUT /memory accepts namespace (move — plain column update, no re-embed) - POST /namespaces/rename: atomic rename; existing target = merge - POST /namespaces/delete: explicit strategy — refuse (default, 409), merge (move to target, name vanishes), deprecate (soft-delete only) - GET /namespaces?with_counts=true adds active/deprecated breakdown - CLI: uteke namespace move|rename|delete (delete requires --confirm) - MCP: uteke_namespace_rename, uteke_namespace_delete, namespace field on uteke_update * test(core): make namespace tests embedder-free for CI (#1183) CI builds enable the onnx feature (via uteke-server's dependency declaration) but the runner has no ORT model file, so Uteke::open()'s default onnx backend fails at first remember(). Use open_with_backend(None) — the same storage-only pattern the server tests use — so the namespace tests exercise DB semantics without an embedder. --------- Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
… chain (#1172) (#1184) * feat(core): provenance data model — source hash, event actor/evidence chain (#1172) Phase 1 of the memory evolution plan (#1172). Additive schema v17 → v18, zero data loss: - memories.source_hash: SHA-256 of content recorded automatically at write time (single shared remember_precomputed path) — auditors recompute it to detect post-write tampering - timeline_events.actor + evidence_json: who performed an event and what evidence supports it (e.g. contradiction resolution, Fase 2 consumer) - Uteke::provenance(id) → ProvenanceReport: provenance fields, trust tier, live-recomputed content hash, and the full event chain - column_exists_in allowlist gains timeline_events (was silently always false, which would double-ALTER in v18 migration) Migration is guarded (column_exists / CREATE IF NOT EXISTS) so legacy stores, fresh stores, and partially-repaired stores all converge to the same shape. Backward compatible: new columns nullable, old binaries unaffected. * feat(surfaces): provenance report on HTTP, CLI, and MCP (#1172) Exposes the phase-1 provenance model on every surface: - GET /provenance?id= — full report (fields, trust tier, hash comparison, event chain); 404 unknown id, 400 missing param - uteke provenance <id> — human-readable audit output with hash verdict (✓ matches / ✗ MISMATCH / — pre-v18 row), --json for tooling - uteke_provenance MCP tool — JSON report for agents Docs: cli-reference (uteke provenance section), api-reference (regenerated), CHANGELOG. --------- Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…) (#1185) Fase 2 of #1172: supersessions become a first-class, auditable ledger. - contradiction_resolutions(): edge-driven ledger (deprecated row + superseded_by edge) — same predicate undo_supersession resolves against, so listed = undoable, always - undo_supersession(): atomic restore + edge-pair removal in one tx (was: promote() committed first, edge delete could fail after → active memory with live superseded_by pair), post-commit vector index re-add + cache invalidation - supersede(): re-supersession refreshes deprecate_reason/deprecated_at so the ledger names the CURRENT winner - Surfaces: GET /contradictions, POST /contradictions/undo, uteke contradictions list|undo, MCP uteke_contradictions(+_undo) - Fix: no-namespace ledger query bound limit to nonexistent ?2 - Tests: core (ledger/undo/chain/resupersession), server API (2), MCP roundtrip (1); fmt/clippy clean; cora review pass Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…1186) * feat: explain recall — ranking signals on every surface (#1160) Add an optional explain mode to recall that shows WHY each memory ranked where it did, on all three surfaces: - Core: Uteke::recall_explained() replays the active strategy's exact pipeline (same channel depths, RRF k=60, weights 1.7/1.0, jaccard, salience/recency, graph rerank) with per-stage instrumentation. Bypasses the recall cache; fts5 works without an embedder, other strategies embed the query once (same cost as a cold recall). - CLI: uteke recall "…" --explain (human-readable + --json) - HTTP: POST /recall with "explain": true; 400 when combined with search_type/at/before/after (memory-only feature) - MCP: explain flag on uteke_recall; loud error with type=all/doc Signals exposed per result: final/base score, vector similarity + rank, FTS rank, RRF score with per-channel fusion contributions (w/(k+rank)), jaccard/salience/recency/graph boost deltas. Tests: core fts5 (CI-safe, score parity with the plain arm), core fusion with the real ONNX embedder (ignored in CI; verified locally — exact score parity, 1.7/61 contribution math, boost consistency), server API (fts5 signals + 400 guards). Live-verified on a dev store: explained output matches the plain pipeline on all surfaces. * fix: explain default invocation + strategy contract (code-scanning #1186) - MCP/CLI: an omitted type (the documented default invocation) is now accepted and treated as memory recall; only explicit all/doc is rejected. The old guard compared the resolved enum (None→All) and wrongly errored on the default call. - core: the vector arm of recall_explained goes through compute_recall like every other strategy, keeping the no-cache/no-double-boost contract consistent; verified live (default --explain works). * fix: hybrid/graph vector sub-channel via compute_recall (code-scanning #1186) The Hybrid|Graph arm fetched its vector sub-channel through the public self.recall entry (cache + dispatcher boosts) while every other arm uses compute_recall — inconsistent with the module contract (cold compute, cache bypassed, boosts applied exactly once at the tail) and able to skew vector_rank with cached/boost-shifted order. Route it through compute_recall like the rest. * fix: reject explain + entity/category/enrich/where/context (code-scanning #1186) Explain mode could not honor entity/category (HTTP), entity/category/ where (CLI), or context/enrich (CLI) — those requests silently returned unfiltered/plain results. Explain is now loud: any flag it cannot represent is rejected with a 400/CLI error instead. --------- Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…1187) (#1190) #1189: graph_data(namespace: None) returned all_nodes() unfiltered — nodes whose parent memory was forgotten or deprecated accumulated in GET /graph forever (soft-delete made this grow on every supersede). Memory-linked nodes are now liveness-filtered (memory exists AND deprecated = 0) in every path, edges touching removed nodes are dropped, and stats counts the filtered graph. #1187: memory-linked graph nodes were labeled with the raw memory UUID. ensure_node_for_memory now labels new nodes with a readable 'preview — short-id' label (short-id keeps labels unique — upsert is label-keyed) and upgrades legacy UUID-labeled rows in place on next access (id preserved, entity nodes untouched). Tests: legacy label upgrade, stale-node filtering (forgotten + deprecated + edge cleanup + stats parity), namespace filter still excludes other namespaces — all embedder-free (remember_precomputed) for CI without ONNX. 666 pass / 0 fail, clippy 0, fmt clean. Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
POST /list accepts include_meta: true and responds with
{memories, total, has_more, next_offset} so clients (corin graph
enrichment, export, bulk sync) can stop blind-paginating with
100-row guesses. next_offset is null on the last page.
Default response is unchanged (bare array) — existing clients are
untouched. include_meta is ignored in at (point-in-time) mode,
which stays a bare array; combining them returns the bare array
rather than erroring, documented in the field docs.
Test: envelope math (total/has_more/next_offset), bare-array
default, last-page shape, at-mode precedence. 667 pass / 0 fail,
clippy 0, fmt clean.
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
#1192) Fase 3 of #1172 closes the epic with an active-store knowledge-update segment proving conflict resolution end-to-end: - contradiction_segment.py: 40 topics x (stale + winner + 3 distractors), semantic 'which X does topic use now?' queries; baseline measured for ALL strategies on the unresolved store, then one resolution pass, then resolved metrics (code-scanning fixes: topic->thing map instead of a broken next() query that malformed questions; baseline-ordering so later strategies never measure a resolved store) - Results (RESULTS.md + results_contradiction_f3/metrics.json): - fusion (default): winner@1 0.850 -> 1.000, MRR 0.925 -> 1.000, stale@5 1.000 -> 0.000 — unresolved conflicts pollute top-5 on every strategy; supersede clears them from the retrieval surface - hybrid baseline ranks the stale fact top-1 on 97.5% of topics (lexical match of 'uses X' with 'use now?'); resolved stale@1 = 0 - ledger listed all 40 resolutions (F2 surface verified in-loop) - uteke supersede <old> <new> [--reason]: CLI surface parity for supersession (was MCP/HTTP only) — prefix-aware, --json, points to contradictions undo for restore Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
Version bump 0.16.0 -> 0.17.0 across the workspace; internal dep requirements (uteke-core/uteke-mcp) bumped to 0.17.0. Minor release theme: inspectable, trustworthy memory. Added: - explain recall on CLI/HTTP/MCP (#1160) - contradiction resolution ledger + undo, edge-driven (#1172 F2) - contradiction benchmark segment + uteke supersede CLI (#1172 F3) - /list pagination metadata envelope (#1188) - provenance data model, schema v18 (#1172 F1) Fixed: - graph excludes stale memory nodes; readable node labels (#1189 #1187) - dual-engine vector layer documented: UTEKE_VECTOR_BACKEND env or [vector] backend in uteke.toml switches usearch/vecq at runtime with automatic index rebuild (#1168, shipped in 0.16.0; now in configuration.md) Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
Collaborator
Author
|
Superseded: develop is protected; the develop->main merge will land via PR #1194-style flow and CI on the merge ref. |
This was referenced Sep 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release PR: develop → main for v0.17.0.
Brings the 0.17.0 release commit (version bump, dated CHANGELOG, dual-engine vector docs) to main so the
v*tag can be cut from main per the release workflow.Version bump commit: b15c2c4 (squash of #1194). Full contents and validation notes in #1194.