Changed
- Project renamed: TokenSave → TraceDecay. The crate, binary, CLI command, and MCP server are now
tracedecay, and the MCP tools are prefixedtracedecay_*. The data directory is now.tracedecay/(an existing.tokensave/directory is still honored as a fallback), and environment variables use theTRACEDECAY_*prefix (legacyTOKENSAVE_*variables are still honored as a fallback). All entries below predate the rename and intentionally retain the historicaltokensave/TokenSavenames. - Version reset for the rebranded crate.
tracedecaynow restarts at0.0.2as a fresh crate line;0.0.1is already occupied on crates.io by the name-reservation placeholder, so0.0.2is the first publishable release number.
Added
-
Local web dashboard (
tokensave dashboard+tokensave_dashboardMCP tool). A self-contained axum server with compile-time-embedded UI assets serving three tabs: Holographic Memory (fact/entity/bank inspector, 2D PCA semantic map, association graph, phase-cosine similarity explorer with brushable histogram, and feature-flagged curation), LCM (overview, FTS search with role/source/session facets, session/node drilldowns, timeline, compression analytics over the global DB), and Code Graph (overview analytics plus a force-directed canvas explorer with search-to-focus, progressive neighbor expansion, callers/callees, filters, and shortest-path mode). CLI flags:--path,--host,--port(0 = auto, parseable URL on stdout),--open. Thetokensave_dashboardMCP tool starts/stops the same server as a background task and returns the URL.GET /api/capabilitiesadvertises feature flags (memory,lcm,graph,curation,llm_curation) for host/UI feature detection. Dark + light themes, responsive down to ~420px. -
Memory curation with hard-delete semantics.
POST /api/plugins/holographic/curateproposes (dry-run) or applies similarity-based deduplication: the lower-trust fact of eachlikely_duplicatepair is permanently deleted via the canonical store path (FK-cascaded entity links, FTS trigger cleanup, bank dirty-marking) — no archive state, no restore.POST /curate/applyexposes a generic delete/merge ops contract for external (e.g. LLM-backed) planners; per-op failures are reported per-op. Migration v13 only cleans up a never-shipped archive-column experiment from local dev databases. -
Hermes wrapper for the dashboard.
dashboard/hermes-wrapper/(canonical; deployed to the hermes-agent working tree) reverse-proxies/holographic/*,/lcm/*, and/graph/*to a spawned or externally configured tokensave dashboard, re-hosts the same UI bundles under the combined "TokenSave" tab, layers an optional LLM curation planner on the/curate/applycontract, and hardens the subprocess lifecycle (stderr drain, parent-death signal, spawn-failure backoff). -
Dashboard build + test infrastructure.
dashboard/npm workspace (esbuild) building all bundles, 16 frontend unit tests (node run-unit-tests.mjs), Playwright smoke (npm run smoke -- --expect-lcm=empty|non-empty), Rust integration suites (tests/dashboard_api_test.rs,dashboard_lcm_fixes_test.rs,dashboard_graph_api_test.rs,mcp_dashboard_tool_test.rs), a dashboard CI job, andbuild.rsrerun-if-changed guards so frontend dist changes force re-embedding. -
Curation previews survive dashboard restarts. The last dry-run curation plan is mirrored to a
.tokensave/dashboard/curation_preview.jsonsidecar and re-hydrated when the server starts; applying curation (or any/curate/applymutation) clears both the in-memory copy and the sidecar. TheGET /curation/previewAPI shape is unchanged, and staleness is still recomputed against the live fact count on every read. -
tokensave install --agent hermesdeploys the dashboard plugin page. The Hermes wrapper (manifest,plugin_api.pyreverse proxy, and the UI bundles — all embedded in the binary, no source checkout needed) is now written to<hermes_home>/plugins/tokensave/dashboard/as part of the default install, where Hermes' dashboard-plugin discovery (stock and forked) picks it up as a "TokenSave" tab with Memory / LCM / Code Graph / Savings sub-tabs. The deployed proxy bakes in the installing binary path and the profile's pinnedproject_rootas spawn-mode defaults (TOKENSAVE_BIN/TOKENSAVE_DASHBOARD_PROJECTenv vars still win); reinstalls preserve the pin,--no-dashboardopts out (and removes a previous deploy), and uninstall cleans the page up. The wrapper also gained the Savings sub-tab (/savings/*proxy to/api/plugins/savings/*). On Hermes versions without dashboard-plugin discovery the deployed directory is inert. -
Dashboard assets build themselves on fresh checkouts. When the embedded
dashboard/*/distbundles are missing,build.rsnow runs the frontend build automatically (npm ci, falling back tonpm install, thennpm run build) with progress reported as build warnings — socargo build/cargo install --path .work from a clean clone. If npm is unavailable, the build still fails fast with actionable instructions.Cargo.tomlswitched to an explicitpackage.includewhitelist that ships the prebuilt dist bundles inside the crate package, makingcargo package/cargo publishverifiable and letting crates.io/docs.rs builds proceed with no Node.js toolchain. The release workflows (release.ymlbuild + publish-crate jobs,release-beta.yml) gained the same dashboard prebuild step as CI. -
Tokenizer-backed cost tier for the Savings & Cost tab (
token-countingfeature, on by default). When transcripts carry no usage counters (all Cursor stores — verified to contain none — plus cline/vibe and any Codex/Claude rows without usage), stored message text is now counted with a real BPE tokenizer (tiktoken-rs,o200k_base/cl100k_base) instead of the chars/4 heuristic: exact for OpenAI-family models, a labeled≈approximation for vendors without a public tokenizer (Claude/Gemini). The API gains a thirdcost_basisvalue"tokenized"(between"actual"and"estimated";"mixed"semantics unchanged), additivetokenizedtoken blocks,tokenized_messagescounts, and per-modeltokenizerprovenance ({"encoder", "exact"}); the UI shows tier badges and an updated methodology note. Counts are cached per message (in-process map + adashboard_token_countssidecar table in the global accounting DB, keyed by message identity with a text-length guard) and pre-warmed in the background at dashboard startup, so 15k+-message stores pay the BPE pass once instead of per request. Disable the feature for a leaner binary (~4 MB embedded vocabularies, lazily decoded) — everything degrades to the chars/4 tier.
Fixed
- The savings ledger records by default again — the Savings tab is no longer empty while lifetime counters grow. The holographic-fact-store commit made the MCP server's global accounting DB opt-in via
TOKENSAVE_ENABLE_GLOBAL_DB, which silently disabledsavings_ledgerwrites (and worldwide-counter flushes) for every default install: tool calls still printedtokensave_metricslines and CLI paths kept growingprojects.tokens_saved, but the dashboard showed "ledger calls: 0 / no events yet". Global accounting is now on by default; opt out withTOKENSAVE_DISABLE_GLOBAL_DB=1(set automatically for cargo-launched processes via.cargo/config.tomlso test runs stay hermetic) orTOKENSAVE_ENABLE_GLOBAL_DB=0, with an explicitTOKENSAVE_ENABLE_GLOBAL_DB=1always winning. The dashboard now also surfaces the gate verdict (savings.recordingin the overview API, arecording: on/offbadge, and an honest explanation when the ledger is empty — including the "restart your MCP server to pick this up" case) instead of an unconditional "no events yet". Covered by a default-on ledger regression test plus env-precedence unit tests; long-running MCP servers must be restarted/reloaded to pick up the fix. - Hermes wrapper spawn mode no longer drops its child server after idle periods. The wrapper's Linux parent-death guard (
PR_SET_PDEATHSIG) fires when the thread that forked the child exits — and FastAPI sync endpoints run on anyio threadpool workers that are reaped after ~10s idle, so the spawnedtokensave dashboardwas SIGTERMed shortly after quiet spells (surfacing as intermittent 502 "connection reset by peer" on the next tab click).plugin_api.pynow spawns from a single long-lived worker thread, binding the child's lifetime to the Hermes host process as intended. - Hermes wrapper cold starts no longer 502. After spawning, the wrapper now waits (bounded, 30s) for the engine's
/api/capabilitiesto answer before proxying the first request, returns a clear503withRetry-Afterif the engine truly fails to come up, and transparently retries GET proxies once after re-resolving the upstream (which reaps and respawns a dead child). POSTs are never retried so curation applies cannot run twice. - Fallback branch DBs are now read-only for sync/index writes.
tokensave sync, lazy single-file syncs, and full indexing now refuse to write when the active git branch is being served from an ancestor branch database, preventing branch-only files from being indexed into the fallback DB. tokensave install --agent hermesgenerates a plugin that loads on newer Hermes hosts. Four generator/installer fixes: (1) the generatedTokenSaveContextEngineimplements the now-abstractupdate_from_response(usage)method (normalizesprompt/input,completion/output, andtotaltoken counts intolast_*_tokensattributes), so plugin load no longer dies withCan't instantiate abstract class; (2) the skill registers under the bare nametokensave— newer Hermes derives the namespace from the plugin and rejects:in skill names; (3) the installer now matches the existing indentation ofplugins.enabled/plugins.disabledlists (Hermes writes 2-space items) instead of always inserting 4-space items, which produced unparseable mixed-indent YAML; and (4) flow-style empty lists (disabled: [], which Hermes itself writes) are accepted instead of failing with "unsupported Hermes plugins config" — an emptyenabled: []is rewritten to a block list. The generated context engine additionally honors aproject_rootconfig key so profiles can pin the indexed project (explicit host kwargs win; the session cwd stays the last fallback).tokensave install --agent hermes --project-root <abs path>pins a profile's plugin to one project. The pin is written into the generated plugin (PINNED_PROJECT_ROOTintools.py): every plugin tool call then passes--project <pin>so memory + LCM stores resolve to<pin>/.tokensave/regardless of the Hermes process cwd, and the context engine uses it ahead of cwd inference (kwargs > config > pin > cwd). Reinstalls without the flag preserve an existing pin; the flag is hermes-only, requires an absolute path, and conflicts with--all-profiles(pins are per-profile).tokensave toolnow walks up from subdirectories to the nearest initialised project when--projectis not given, matching howsync,status,serve, anddashboardresolve project roots.- Cursor hook hints use the quote-aware shell parser.
tool_hintsclassified search commands with a naivesplit_whitespace, so a quoted pattern likegrep "needle -r" fileleaked a fake-rflag and misclassified as a recursive search. It now shareshooks.rs's quote/escape-awareshell_wordsparser (single shared implementation, regression-tested). - Hermes generated plugin files are written atomically.
write_text_filenow uses the write-to-.new-then-rename pattern (like the config writer), so a mid-write crash can no longer leave a truncated__init__.py/tools.pybehind. Unsupported-config errors during install also name the exact retry command (tokensave install --agent hermes). - v13 archive-column cleanup handles generated-column dev databases. The migration enumerated columns with
PRAGMA table_info, which hides GENERATED columns — so a dev DB where the abandoned archive revision leftsuperseded_byas a generated column referencingmerged_intoskipped that drop and then failed withno such column: merged_into. The migration now usesPRAGMA table_xinfoand drops the columns in reverse-addition order (dependent generated columns first). Covered by a regression test seeding exactly that odd state. - Archive-semantics purge (policy: deleted memories are permanently hard-deleted). Removed the last UI remnants of the never-shipped archive feature: the CurationPanel no longer recognizes/renders
archiveorsupersedeops (neither planner can produce them; the curate ops contract is delete/merge only), thearchiveaction field is gone from the frontend types, and stale Hermes-wrapper docstrings namingarchive/archive/{fact_id}/restoreroutes were corrected. A new store-level test pins the full hard-delete cascade:MemoryStore::remove_factremoves the fact row, its FTS mirror row, its entity links, and its feedback events, and marks the fact's banks dirty.