Compare commits

..

28 commits

Author SHA1 Message Date
Sam Valladares
16903f3ab4 Merge pull request #62 from delandtj/design-adr-0002-phase-2-execution 2026-06-15 16:25:38 -05:00
Sam Valladares
31890278d3 Merge pull request #65 from samvallad33/release/v2.1.24-data-dir-permissions 2026-06-15 15:59:12 -05:00
Sam Valladares
47de61f2d2 feat(config): Phase 2 Configurable Output — vestige.toml + output profiles (v2.1.26)
Rebased on v2.1.25 merge/supersede and bumped the post-release metadata to v2.1.26 so this branch does not roll versions backward.

Adds local vestige.toml defaults, output profiles, and MCP response precedence for search, timeline, codebase context, and session context.

Verified:
- cargo metadata --format-version 1 --locked --no-deps
- cargo test -p vestige-core config --no-fail-fast
- cargo test -p vestige-mcp config --no-fail-fast
2026-06-15 13:51:50 -05:00
brendon
51f08264f7
fix(storage): tolerate SQLite-native datetime format in parse_timestamp
Tolerate SQLite-native timestamps from external writers while preserving RFC3339 as the canonical write format.

Verified locally on the merge result:
- cargo test -p vestige-core test_parse_timestamp_accepts_rfc3339_and_sqlite_native --no-fail-fast

CI/Test Suite on the updated PR branch are green.
2026-06-15 13:50:55 -05:00
Sam Valladares
c23d7a309c
feat(merge-supersede): Phase 3 — diff-previewed, reversible merge/supersede controls (v2.1.25) (#75)
Adds opt-in, preview-first combine/dedupe/supersede on a never-delete
(bitemporal) store. The default is review, never silent mutation. Every applied
operation is recorded as a reversible, auditable event with provenance — a git
reflog for your agent's memory.

Core (vestige-core):
- advanced::merge_supersede — pure Fellegi-Sunter two-threshold scoring
  (embedding + tag + token Jaccard), match/possible/non_match classification,
  plan/diff and operation-log types, merge-composition helpers. Unit-tested.
- storage: merge_candidates, plan_merge, plan_supersede, apply_plan, merge_undo,
  protect/pin, and per-project merge_policy (persisted in fsrs_config, env
  overridable). Supersede invalidates bitemporally (valid_until + superseded_by,
  Graphiti-style "invalidate, don't delete") and keeps the old node queryable.
- Migration V14: merge_plans + merge_operations tables, knowledge_nodes.protected
  and .superseded_by columns + indexes. Idempotent on replay (duplicate-column
  guarded ADD COLUMNs).

MCP (vestige-mcp):
- Seven new tools registered + dispatched: merge_candidates, plan_merge,
  plan_supersede, apply_plan, merge_undo, protect, merge_policy.
- apply_plan requires confirm=true for possible/non_match plans; match plans
  auto-apply only when policy.auto_apply is set (default off).

Tests: candidate-threshold classification, plan-preview makes no mutation,
apply+undo reversibility, supersede bitemporal invalidation preserves old-node
queryability, protect blocks merge-away, low-confidence requires confirm, policy
roundtrip, migration V14 + idempotent replay. All 796 scoped tests pass; clippy
-D warnings clean on touched crates.

Docs: docs/MERGE_SUPERSEDE.md + CHANGELOG entry. Version bump 2.1.23 -> 2.1.25.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 12:55:31 -05:00
Luc Lauzon
b01269db22
feat(mcp/system_status): add optional schema_introspection flag (#69)
* feat(mcp/system_status): add optional schema_introspection flag

Adds an optional `schema_introspection: bool` parameter to the
`system_status` MCP tool. When set to true, the response gains a
`schema` block carrying:

- `schemaVersion` (u32) — highest applied migration, mirrors
  `Storage::current_schema_version` (now exposed via a typed public
  method).
- `schemaVersionAppliedAt` (RFC3339, optional) — timestamp the
  current schema_version row was applied.
- `tables` ([{name, rows, columns}]) — per-table row count + column
  list, walked over the canonical PORTABLE_USER_DATA_TABLES set so
  the surface stays stable across migrations rather than enumerating
  arbitrary sqlite_master rows.
- `embeddingNullCount` (i64) — count of knowledge_nodes with NO row
  in node_embeddings. Distinct from MemoryStats.nodes_with_embeddings
  (which keys off the `has_embedding` flag column), so audit scripts
  can detect drift between the flag and the join-based truth.
- `activeEmbeddingModel` (string, optional) + `activeEmbeddingDimensions`
  (u32, optional) — mirrors the existing MemoryStats active-model
  fields, included here so audits get schema_version + active model
  in a single round-trip.

Motivation: external consumers (audit scripts, migration guards,
downstream binary upgrade scripts) currently must read SQLite
directly to learn the schema shape, which couples them to internals
Vestige owns and breaks on every migration. This PR closes that gap
with a first-class MCP surface.

Implementation:

- New `pub fn schema_introspection() -> Result<SchemaIntrospection>`
  inherent method on `Storage` (sqlite.rs). Inherent, not on a
  trait — schema-walk is SQLite-specific by nature, so this stays
  out of any future MemoryStore trait extraction.
- New typed structs `SchemaIntrospection` + `TableIntrospection` in
  memory/mod.rs (canonical home alongside MemoryStats), re-exported
  from the crate root.
- MCP layer (maintenance.rs) parses `SystemStatusArgs`, conditionally
  extends the existing response object with a `schema` key — additive,
  default off, response shape unchanged when omitted.

Coupling assessment vs PR #61 (storage-trait-phase1):

This PR adds ONE new public inherent method on `Storage` plus uses
three already-existing private helpers (`current_schema_version`,
`table_exists`, `table_row_count`, `table_columns`). It does NOT
touch the existing inherent method signatures, does NOT add anything
to the prospective `MemoryStore` trait surface, and does NOT modify
any of the ~25 methods #61 lifts into the trait. PR #61 is purely
additive on the trait surface (per its description, `pub type
Storage = SqliteMemoryStore;` preserves all existing call sites);
this PR is additive on the inherent surface. Two purely-additive
changes to disjoint surfaces should rebase cleanly.

Tests:
- system_status_schema_has_schema_introspection_flag (schema
  introspection: property present, type=boolean, default=false,
  not required)
- system_status_without_schema_flag_omits_schema_block
  (backwards-compat: unset/false → no `schema` key)
- system_status_with_schema_flag_emits_schema_block (positive case:
  schema block present, schemaVersion >= 13, tables non-empty,
  knowledge_nodes row count + columns sane, convenience fields
  present)
- system_status_camelcase_alias (#[serde(rename_all="camelCase")] +
  alias works for both snake and camel input)
- storage_schema_introspection_method (Storage-layer method tested
  directly, independent of MCP)

Closes the second of two gaps surfaced in the knowledge-mgmt-sota-uplift
initiative. Companion to PR #68 (search.tag_prefix). The two PRs are
deliberately decoupled — this one carries the storage-layer surface
extension; the other is MCP-layer-only.

* fix(memory): derive Default on SchemaIntrospection to satisfy clippy

The manual `impl Default for SchemaIntrospection` tripped
`clippy::derivable_impls` under the workspace's `-D warnings` CI gate.
All fields are types with `Default` impls (`u32`, `Option<T>`, `Vec<T>`,
`i64`), so deriving is equivalent and clippy-clean. Matches the existing
style of `ConsolidationResult` further down in the same file.
2026-06-11 14:24:42 -05:00
Luc Lauzon
5aa261398d
feat(mcp/search): add optional tag_prefix post-filter (#68)
Adds an optional `tag_prefix` string parameter to the `search` MCP tool.
When set, only results that carry at least one tag whose value starts
with the prefix are returned (case-sensitive, matching the existing
exact-tag semantics in memory_timeline / export / gc).

Motivation: external consumers that need "all memories tagged
`<class>:*`" (e.g. `meeting:standup`, `meeting:1-on-1`) currently have
three paths, all bad: (i) export everything and filter client-side
(heavy), (ii) enumerate the prefix space and pass exact tags as a list
(impractical for open-set tag classes), or (iii) read SQLite directly
(an anti-pattern that couples consumers to internal schema). This PR
closes that gap with a minimal, additive surface.

Implementation note: filter runs at the MCP layer, NOT in the storage
predicate. Rationale: (a) leaves crates/vestige-core/src/storage/
untouched, avoiding collision with PR #61's storage-trait extraction;
(b) `SearchResult.node.tags` is already loaded from the same JSON-array
column the brief's proposed SQL would scan, so the post-filter is
functionally equivalent; (c) post-filter applies BEFORE the reranker
so the cross-encoder does not waste cycles on memories the caller will
not receive, and BEFORE strengthen-on-access so dropped results do not
get a testing-effect boost they did not earn.

Headroom: when tag_prefix is set, the hybrid path doubles its overfetch
multiplier (capped at the existing 100 ceiling) and the concrete path
fetches 3x its normal limit, both to leave the post-filter enough pool
to still return ~limit results after thinning. The Stage 0
keyword-priority merge also re-applies the prefix filter so it cannot
re-introduce filtered-out memories.

Backwards-compat: parameter is optional, defaults to None; every existing
call shape and response shape is unchanged.

Tests:
- tags_match_prefix unit (prefix-vs-substring, case-sensitivity,
  tagless-memory semantics, empty-prefix corner case)
- schema introspection (property present, type=string, not required)
- hybrid-path filter excludes non-matching tag-classes
- hybrid-path filter excludes tagless memories
- backwards-compat: no tag_prefix → behavior unchanged
- concrete-path filter (literal-query branch) honors tag_prefix

Closes a gap surfaced in the knowledge-mgmt-sota-uplift initiative
(KMSU Session 89 audit; ~3,300-memory production Vestige).
2026-06-11 14:24:33 -05:00
Sam Valladares
241dfdd6cb
Merge pull request #67 from ShalokShalom/patch-1
Update CONFIGURATION.md with Opencode TUI details
2026-06-08 12:14:26 -05:00
Sam Valladares
603efdcb2c
Merge branch 'main' into patch-1 2026-06-08 12:13:09 -05:00
Sam Valladares
a355da99a6 Revert "Add developer launch kit for Vestige v2.1.23"
This reverts commit 00511948ff.
2026-06-02 12:40:48 -05:00
Sam Valladares
6a37586c5f Revert "Fix Pages workflow enablement and add Lobsters launch copy"
This reverts commit 67f9550e6c.
2026-06-02 12:40:48 -05:00
Sam Valladares
67f9550e6c Fix Pages workflow enablement and add Lobsters launch copy
Use configure-pages enablement so the landing deploy can succeed without
manual repo setup first.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 12:39:26 -05:00
Sam Valladares
00511948ff Add developer launch kit for Vestige v2.1.23
Dual-wave marketing assets (Receipt Lock + cognitive memory), GitHub Pages
landing, comparison doc, ready-to-post copy, growth-engine scripts, and a
dedicated marketing Vestige data-dir setup path.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 12:38:18 -05:00
ShalokShalom
d8de7daf11
Update CONFIGURATION.md
Add link to specific options to place the config file
2026-06-02 16:08:39 +02:00
ShalokShalom
c9f457dd94
Update CONFIGURATION.md with Opencode TUI details
Added configuration instructions for Opencode TUI/Desktop.
2026-06-02 16:06:08 +02:00
Sam Valladares
cd496e562c docs: add Glama ownership metadata 2026-05-28 16:37:33 -05:00
Sam Valladares
8c18231a20 docs: keep README focused on v2.1.23 2026-05-28 13:34:09 -05:00
Sam Valladares
3df930ca7e Fix data-dir permission preservation 2026-05-27 20:00:46 -05:00
Sam Valladares
14b061f124
Release v2.1.23 Receipt Lock hardening
Hardens Sanhedrin Receipt Lock for model-agnostic use, adds fail-open telemetry and receipt docs, fixes smart_ingest batch safety, wires opt-in CUDA Qwen3 device selection, and refreshes dashboard/release assets.\n\nFixes #54\nFixes #58\nFixes #60\nRefs #59
2026-05-27 19:03:16 -05:00
Jan De Landtsheer
21f0b29bae
docs: rewrite local-dev-postgres-setup for container approach; bump pg16 -> pg18
Land the Postgres dev cluster recipe Jan provisioned on delandtj-home
(rootless podman + pgvector/pgvector:pg18, PG 18.4, pgvector 0.8.2) and
align all live ADR 0002 / Phase 2 sub-plan references from pg16 to pg18.

- docs/plans/local-dev-postgres-setup.md -- rewritten end-to-end:
  podman container vestige-pg with --restart=always, named volume
  vestige-pgdata, PGDATA=/var/lib/postgresql/data/pgdata, port mapping
  127.0.0.1:5432:5432, two-password split (superuser + app role),
  pgvector preinstalled, CREATE EXTENSION vector handled at setup,
  day-to-day commands, password rotation, dev-grade backup/restore,
  teardown, boot-persistence notes for rootless podman. Old native
  Arch install recipe moved to Out-of-scope (covered by image now).

- docs/adr/0002-phase-2-execution.md -- the open-thread mention of
  pgvector/pgvector:pg16 in the Follow-ups section now reads pg18.

- docs/plans/0002c-migrations.md -- container example in the local
  dev section updated to pg18.

- docs/plans/0002d-store-impl-bodies.md -- testcontainers GenericImage
  tag pg16 -> pg18; prose reference updated.

- docs/plans/0002h-testing-and-benches.md -- harness pg18 across
  testcontainers Postgres builder, image-caching prose, CI workflow
  example.

The archival master plan (docs/plans/0002-phase-2-postgres-backend.md)
keeps its original pg16 references intentionally; the supersession
notice already points readers to the live sub-plans.
2026-05-27 15:09:23 +02:00
Jan De Landtsheer
fc0681ed0f
Merge branch 'main' into design-adr-0002-phase-2-execution 2026-05-27 14:50:59 +02:00
Jan De Landtsheer
9ef8afdb20
docs(plans): add Phase 2 sub-plans 0002a-0002i + supersession notice
Nine Phase 2 sub-plans operationalising ADR 0002 against the Phase 2
master plan, each sized to fit a focused implementation session and
handed to Claude Code as a /goal brief without requiring the agent to
load the master plan.

Order of execution (each depends on the previous unless noted):
- 0002a-skeleton-and-feature-gate.md -- postgres-backend Cargo feature
  + PgMemoryStore skeleton with todo!() bodies. D1+D2.
- 0002b-pool-and-config.md -- PgPool builder, VestigeConfig/
  PostgresConfig, vestige.toml loader wired into vestige-mcp. D3+D7
  (master plan numbering).
- 0002c-migrations.md -- sqlx migrations 0001_init/0002_hnsw including
  D7 (users/groups/memberships, owner/visibility/shared_with_groups)
  and D8 (codebase column). SQLite V15 parity migration. D4.
- 0002d-store-impl-bodies.md -- real CRUD + registry bodies; trivial
  fts_search/vector_search bodies. D2+D6.
- 0002e-hybrid-search.md -- one-statement RRF query. D5.
- 0002f-migrate-cli.md -- vestige migrate copy (SQLite -> Postgres),
  --dry-run, idempotent re-runs, --allow-source-upgrade for pre-V15
  sources. D8+D10.
- 0002g-reembed.md -- vestige migrate reembed (offline rebuild).
  D9 + D10 reembed arm. Ships resolve_embedder helper as a workaround
  for the missing Embedder::from_name(&str) constructor.
- 0002h-testing-and-benches.md -- testcontainers harness, six
  integration test files, Criterion bench at 1k/100k. D14+D15.
- 0002i-runbook.md -- operator-facing deployment + day-2 runbook. D16.

Supersession notice added to the master plan (0002-phase-2-postgres-
backend.md) pointing at ADR 0002; body retained as archival reference.

PR B carries this commit plus the previous two (ADR 0002 + Phase 1
amendment sub-plans); no code change.
2026-05-27 09:35:58 +02:00
Jan De Landtsheer
697ade5b02
docs(plans): add Phase 1 amendment sub-plans 0001a/b/c
Three sub-plans operationalising ADR 0002 D1 + D3 on the existing
feat/storage-trait-phase1 branch (790c0c8, not yet pushed upstream):

- 0001a-trait-rewrite.md -- rewrite MemoryStore with
  #[trait_variant::make(MemoryStore: Send)] generating a non-Send
  LocalMemoryStore companion. Production callers use Arc<Storage> and are
  unaffected; only the trait declaration and SQLite impl block change.
- 0001b-sqlite-split.md -- pure code motion. Split sqlite.rs (8713 lines)
  into a sqlite/ directory (mod, crud, search, scheduling, graph, domain,
  registry, portable_sync, trait_impl). Public re-exports unchanged; tests
  green commit-by-commit. Depends on 0001a so trait_impl.rs picks up the
  trait_variant attribute once.
- 0001c-async-trait-sunset.md -- rewrite Embedder the same way, then
  remove async-trait = "0.1" from crates/vestige-core/Cargo.toml. End
  state: zero async_trait references in the workspace.

Together these three lands as PR A.
2026-05-27 09:35:37 +02:00
Jan De Landtsheer
c584ec8afe
docs(adr): add ADR 0002 -- Phase 2 execution
Binding ADR for Phase 2 Postgres backend integration plus the Phase 1
amendment that removes async_trait from the storage and embedder traits.

Decisions D1-D8:
- D1: sunset async_trait across MemoryStore + Embedder via trait_variant
- D2: PgMemoryStore::connect(url, max_connections) mirrors SqliteMemoryStore;
  no Embedder in constructor; register_model handles pgvector typmod
- D3: split sqlite.rs into a sqlite/ directory as Phase 1 amendment
- D4: postgres/ as a directory from day one
- D5: sub-plan layout -- 3 Phase 1 amendment + 9 Phase 2 sub-plans
- D6: no separate ADR for the SQLite split (pure code motion)
- D7: reserve multi-tenancy schema (users/groups/group_memberships +
  owner_user_id/visibility/shared_with_groups) in Phase 2 so Phase 3 auth
  is additive, not an online migration over an HNSW-indexed table
- D8: codebase promoted to a first-class indexed column on knowledge_nodes;
  mcp_client_id and session_id stay in metadata JSONB

PR cadence: PR A = Phase 1 amendment (code on feat/storage-trait-phase1);
PR B = this ADR + Phase 2 sub-plans (docs only); PR C = Phase 2
implementation. Phase 4 sharing_rules table sketched in Follow-ups.
2026-05-27 09:35:23 +02:00
Luc Lauzon
a8550410b0
feat(mcp): add per-tool _meta["anthropic/maxResultSizeChars"] annotation (#56)
Claude Code v2.1.91+ honors the per-tool annotation
`_meta["anthropic/maxResultSizeChars"]` (up to 500_000) to override
its 50K default truncation of `CallToolResult`. Without it, large
Vestige payloads are silently truncated and spilled to disk, forcing
the parent agent to chunk-read them.

Empirically observed truncation under realistic default parameters
(measured on v1.3.0 against ~3,300 memories; v2.x tool surface
preserves the same names + payload shapes):

  search(detail_level="full", limit=20)  -> 134,824 chars  -> truncated
  search(detail_level="summary", limit=10) ->  71,318 chars -> truncated
  memory_timeline(limit=30)              ->  83,626 chars  -> truncated

This patch:

1. Adds `meta: Option<serde_json::Value>` to `ToolDescription` with
   `#[serde(rename = "_meta")]` so the wire shape matches the MCP
   spec. Backwards-compatible (the field is optional +
   `skip_serializing_if`; older MCP clients ignore unknown JSON keys
   per the spec).

2. Derives `Default` on `ToolDescription` so existing call sites can
   adopt the new field via struct-update syntax
   (`..Default::default()`) without restating it.

3. Annotates the four high-payload tools per measurement-driven
   discipline; the other 21 tools deliberately do NOT carry the
   annotation (cargo-cult prevention — a generous cap on every tool
   dilutes the signal and trains future maintainers that the value
   is arbitrary):

   - search           -> 300_000 (2.2x headroom over observed peak)
   - memory_timeline  -> 200_000 (2.4x headroom over observed peak)
   - memory           -> 100_000 (single-record bounded)
   - codebase         -> 100_000 (future-growth bounded)

   Tools that COULD plausibly grow into the annotated set with future
   workload (`deep_reference`, `cross_reference`, `memory_graph`,
   `explore_connections`, `session_context`) are left unannotated
   until empirical measurement shows truncation under realistic use.

4. Adds three regression tests in `server::tests`:
   - test_high_payload_tools_have_max_result_size_annotation:
     pins each cap value + asserts <= 500K Anthropic ceiling
   - test_other_tools_do_not_carry_max_result_size_annotation:
     cargo-cult prevention; dynamically iterates `tools/list` and
     asserts every tool NOT in the discipline-prescribed set lacks
     the annotation (robust to new tools being added by future PRs)
   - test_meta_wire_shape_uses_underscore_meta_field:
     pins the serde rename to `_meta` (the spec'd wire name) so a
     refactor of `ToolDescription` cannot silently drop the rename

All 22 `server::tests` pass on v2.1.22 base (19 pre-existing + 3 new).
Full lib test suite: 379/380 pass; the 1 unrelated failure
(`tools::maintenance::tests::test_portable_export_writes_archive_to_storage_exports_dir`)
is a pre-existing Windows path-separator assertion bug in
`tools/maintenance.rs:823` (`path.ends_with("exports/portable-test.json")`
fails on Windows where the path uses `\`) — unaffected by this PR.

References:
- Anthropic CC v2.1.91 release notes (April 2026): "Added MCP tool
  result persistence override via _meta['anthropic/maxResultSizeChars']
  annotation (up to 500K), allowing larger results like DB schemas
  to pass through without truncation"
- claude-agent-sdk-python v0.1.55 #756: forward bookkeeping
  establishing the on-Tool-definition (not on-CallToolResult)
  semantics for this annotation

Co-authored-by: Peter Lauzon <inbijiburu@protonmail.com>
2026-05-25 13:49:51 -05:00
Sam Valladares
1399329810
Release v2.1.22 Sanhedrin receipts (#55) 2026-05-25 01:44:52 -05:00
Jan De Landtsheer
9c633c172b
Added postgres admin
added amends to the postgres backend/phase2
2026-04-22 12:10:39 +02:00
Jan De Landtsheer
0d273c5641
docs: ADR 0001 + Phase 1-4 implementation plans
Pluggable storage backend, network access, and emergent domain
classification. Introduces MemoryStore + Embedder traits, PgMemoryStore
alongside SqliteMemoryStore, HTTP MCP + API key auth, and HDBSCAN-based
domain clustering. Phase 5 federation deferred to a follow-up ADR.

- docs/adr/0001-pluggable-storage-and-network-access.md -- Accepted
- docs/plans/0001-phase-1-storage-trait-extraction.md
- docs/plans/0002-phase-2-postgres-backend.md
- docs/plans/0003-phase-3-network-access.md
- docs/plans/0004-phase-4-emergent-domain-classification.md
- docs/prd/001-getting-centralized-vestige.md -- source RFC
2026-04-22 12:10:24 +02:00
200 changed files with 24088 additions and 512 deletions

3
.gitignore vendored
View file

@ -139,3 +139,6 @@ apps/dashboard/node_modules/
# =============================================================================
fastembed-rs/
.mcp.json
.claude/
.codebase-memory/

View file

@ -7,6 +7,152 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [2.1.26] - 2026-06-15 — "Configurable Output"
Roadmap **Phase 2: Configurable Output**. Users can now control the default
shape and size of high-traffic MCP responses with an optional, local-first
config file — without recompiling and without a cloud service. The default
behavior is unchanged: a fresh install with no `vestige.toml` behaves exactly
as before.
### Added
- **`vestige.toml` config file**, loaded from the active Vestige data directory
(`<data_dir>/vestige.toml`, alongside `vestige.db`). A missing or malformed
file falls back to built-in defaults, so existing installs are unaffected.
- **`[defaults]` table** with three keys: `detail_level`
(`brief` | `summary` | `full`), `limit` (default result count for
high-traffic tools), and `profile`.
- **Output profiles**`lean`, `default`, `audit`, `research` — each presetting
a coherent bundle of detail level, result limit, and whether scores and
timestamps are included:
- `lean`: `brief` detail, limit 5, scores and timestamps dropped (smallest
context cost).
- `default`: historical behavior — `summary` detail, tool's own default
limit, scores and timestamps present. **Unchanged.**
- `audit`: `full` detail with every field, score, and timestamp.
- `research`: `full` detail with a larger default limit (25).
- **Three-layer precedence**, applied per call: an explicit MCP parameter wins
over the config file, which wins over the built-in default.
- **`profile` field** echoed in `search`, `memory_timeline`, `codebase`
(`get_context`), and `session_context` responses so the active profile is
observable.
### Changed
- `search`, `memory_timeline`, `codebase` (`get_context`), and
`session_context` now resolve their default detail level and result limit
through the config file when no explicit parameter is supplied. With no
`vestige.toml` present, their output is byte-for-byte identical to v2.1.25.
### Documentation
- `docs/CONFIGURATION.md` gains a **Output Configuration (`vestige.toml`)**
section documenting the file location, `[defaults]` keys, profile presets,
and precedence rules.
## [2.1.25] - 2026-06-12 — "Merge / Supersede Controls"
v2.1.25 ships Phase 3: diff-previewed, confidence-gated, reversible,
self-explaining combine/dedupe/supersede on a never-delete (bitemporal) store.
The default is always preview/review — these tools never silently mutate memory.
The differentiator is the reversible operation log: every merge/supersede/undo is
an auditable, reversible event with provenance ("why did these combine?") — a git
reflog for your agent's memory.
### Added
- **Seven new MCP tools** for merge/supersede control:
- `merge_candidates` — surface likely duplicate/overlapping clusters with
confidence scores and the signals behind each (Fellegi-Sunter
match/possible/non-match). Read-only.
- `plan_merge` — produce a previewable merge PLAN (a diff of combined
content/tags/provenance) without applying it.
- `plan_supersede` — preview superseding A with B (bitemporal invalidation,
audit-preserving) without applying.
- `apply_plan` — execute a previously-generated plan id; recorded as a
reversible operation.
- `merge_undo` — reverse a prior merge/supersede operation, or list the
reversible operation log (the "memory reflog").
- `protect` — pin a memory so it can never be auto-merged, superseded, or
garbage-collected.
- `merge_policy` — get/set the per-project Fellegi-Sunter two thresholds
(`match_threshold`, `possible_threshold`) and `auto_apply`.
- **Bitemporal "invalidate, don't delete" supersede** (Graphiti-style): a
superseded memory is kept and stays queryable for audit. It is stamped with
`valid_until = now` and a new `superseded_by` lineage pointer, instead of being
deleted or merely demoted.
- **Reversible operation log** (`merge_operations` table) — every applied
merge/supersede records an undo payload and provenance signals so any operation
can be reversed, including restoring survivor content/tags and clearing the
bitemporal invalidation.
- **Fellegi-Sunter two-threshold scoring** for dedup/merge candidates, combining
embedding cosine similarity with tag and content-token overlap. Borderline
"possible" matches are surfaced for review instead of force-merged.
- **Memory protection / pinning**`protected` column on `knowledge_nodes`;
protected memories are excluded from auto-merge/supersede/GC paths.
- **Migration V14** adding the `merge_plans` and `merge_operations` tables, the
`protected` and `superseded_by` columns on `knowledge_nodes`, and their
indexes. Idempotent on replay.
- **Docs**: `docs/MERGE_SUPERSEDE.md` describing the design, the bitemporal
model, the two-threshold policy, the reversible operation log, and the tool
surface.
### Notes
- All merge/supersede operations are **opt-in and preview-first**. `apply_plan`
requires `confirm=true` for `possible`/`non_match` plans, and only applies
`match` plans without confirmation when `merge_policy.auto_apply` is enabled
(default off). This deliberately avoids the silent-merge / auto-delete /
audit-trail-loss anti-patterns reported against other memory systems.
- The merge policy persists per-project and is also overridable via
`VESTIGE_MERGE_MATCH_THRESHOLD`, `VESTIGE_MERGE_POSSIBLE_THRESHOLD`, and
`VESTIGE_MERGE_AUTO_APPLY` environment variables.
## [2.1.23] - 2026-05-27 — "Receipt Lock Hardening"
v2.1.23 hardens the Sanhedrin launch path so Receipt Lock is portable,
observable, and precise enough for broader use.
### Added
- **Model-agnostic Sanhedrin backend presets** for custom OpenAI-compatible
servers, small laptops, Ollama, MLX, vLLM, llama.cpp, hosted APIs, and
Anthropic via LiteLLM. Sanhedrin no longer guesses a large default verifier.
- **Fail-open telemetry** in `fail-open.jsonl`, plus a dashboard telemetry API
and 7-day ambient dashboard counters for vetoes, appeals, and fail-open runs.
- **Receipt schema documentation** covering receipt artifacts, appeals, command
ledgers, fail-open logs, compatibility rules, and staged-evidence trust
boundaries.
- **Opt-in CUDA feature flags** for Qwen3 embedding builds on NVIDIA hardware.
### Changed
- Receipt Lock strips code fences, inline code, blockquotes, quoted regions, and
scoped epistemic hedges before matching verification claims.
- Structured transcript tool-use receipts are the default evidence path; loose
JSON command scanning now requires `VESTIGE_SANHEDRIN_ALLOW_LOOSE_LEDGER=1`.
- Claim-mode sampling now prioritizes higher-severity claims instead of taking
the first eight source-order claims.
- Hosted Sanhedrin credentials now require `VESTIGE_SANHEDRIN_API_KEY` and are
only sent to the configured Sanhedrin endpoint, never to Vestige retrieval.
- `smart_ingest` batch mode now defaults to `batchMergePolicy: "force_create"`
so caller-separated items stay separate unless callers opt into smart merging.
- CUDA-enabled Qwen3 builds now try `Device::new_cuda(0)` before falling back to
Metal or CPU.
### Fixed
- Standalone dashboard mode now hydrates the cognitive engine for Dream and
Deep Reference instead of returning 503s.
- `--data-dir` now rejects existing non-directory paths with a clear error.
- `vestige-restore` now handles `--help` and `--version` instead of treating
them as backup file paths.
- Smart ingest merge/update responses now include `previousContent`,
`mergedFrom`, and `mergePreview` so callers can inspect mutated memories.
- Daily Sanhedrin telemetry now preserves NOTE and CAUTION buckets instead of
folding both into PASS.
## [2.1.22] - 2026-05-25 — "Sanhedrin Receipts"
v2.1.22 makes the optional Sanhedrin hook quieter and more accountable by

120
Cargo.lock generated
View file

@ -392,8 +392,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bd9895436c1ba5dc1037a19935d084b838db066ff4e15ef7dded020b7c12a4a"
dependencies = [
"byteorder",
"candle-kernels",
"candle-metal-kernels",
"candle-ug",
"cudarc 0.19.7",
"float8",
"gemm 0.19.0",
"half",
@ -413,6 +415,15 @@ dependencies = [
"zip",
]
[[package]]
name = "candle-kernels"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "742e2ac226b777134436e9e692f44e77c278b8a7abb1554dc10e44dc911b349f"
dependencies = [
"cudaforge",
]
[[package]]
name = "candle-metal-kernels"
version = "0.10.2"
@ -453,6 +464,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca0fc3167cbc99c8ec1be618cb620aa21dca95038f118c3579a79370e3dc5f77"
dependencies = [
"ug",
"ug-cuda",
"ug-metal",
]
@ -771,6 +783,46 @@ dependencies = [
"typenum",
]
[[package]]
name = "cudaforge"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f7a0d45b139b5beeeb1c34188717e12241c44a0120afb498815ce7f5373c691"
dependencies = [
"anyhow",
"fs2",
"glob",
"num_cpus",
"rayon",
"serde",
"serde_json",
"sha2",
"thiserror 2.0.18",
"walkdir",
"which",
]
[[package]]
name = "cudarc"
version = "0.17.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf99ab37ee7072d64d906aa2dada9a3422f1d975cdf8c8055a573bc84897ed8"
dependencies = [
"half",
"libloading 0.8.9",
]
[[package]]
name = "cudarc"
version = "0.19.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cea5f10a99e025c1b44ae2354c2d8326b25ddbd0baf76bde8e55cfd4018a2cc"
dependencies = [
"float8",
"half",
"libloading 0.9.0",
]
[[package]]
name = "cxx"
version = "1.0.194"
@ -1034,6 +1086,12 @@ dependencies = [
"syn",
]
[[package]]
name = "env_home"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe"
[[package]]
name = "equator"
version = "0.4.2"
@ -1254,6 +1312,16 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "fs2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "fsevent-sys"
version = "4.1.0"
@ -1632,6 +1700,12 @@ dependencies = [
"url",
]
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "h2"
version = "0.4.13"
@ -3752,6 +3826,17 @@ dependencies = [
"digest",
]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
@ -4327,6 +4412,19 @@ dependencies = [
"yoke 0.7.5",
]
[[package]]
name = "ug-cuda"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f0a1fa748f26166778c33b8498255ebb7c6bffb472bcc0a72839e07ebb1d9b5"
dependencies = [
"cudarc 0.17.8",
"half",
"serde",
"thiserror 1.0.69",
"ug",
]
[[package]]
name = "ug-metal"
version = "0.5.0"
@ -4531,7 +4629,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "vestige-core"
version = "2.1.22"
version = "2.1.26"
dependencies = [
"candle-core",
"chrono",
@ -4567,7 +4665,7 @@ dependencies = [
[[package]]
name = "vestige-mcp"
version = "2.1.22"
version = "2.1.26"
dependencies = [
"anyhow",
"axum",
@ -4792,6 +4890,18 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]]
name = "which"
version = "7.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762"
dependencies = [
"either",
"env_home",
"rustix",
"winsafe",
]
[[package]]
name = "winapi"
version = "0.3.9"
@ -5058,6 +5168,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "winsafe"
version = "0.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]]
name = "wit-bindgen"
version = "0.51.0"

View file

@ -10,7 +10,7 @@ exclude = [
]
[workspace.package]
version = "2.1.22"
version = "2.1.26"
edition = "2024"
license = "AGPL-3.0-only"
repository = "https://github.com/samvallad33/vestige"

166
README.md
View file

@ -20,117 +20,18 @@ Built on proven memory and retrieval ideas — FSRS-6 spaced repetition, predict
---
## What's New in v2.1.22 "Sanhedrin Receipts"
## What's New in v2.1.23 "Receipt Lock Hardening"
v2.1.22 makes the optional Sanhedrin hook accountable enough to trust in daily
agent work. Vetoes now leave local receipts, verification claims need real
command evidence, and users can appeal stale or over-strict blocks from the
dashboard.
v2.1.23 turns the Sanhedrin Receipt Lock launch into something more portable,
observable, and harder to spoof.
- **Receipt Lock.** Claims like "tests passed", "build is green", or "lint is clean" are blocked unless the current transcript contains a matching successful command receipt.
- **Screenshotable veto receipts.** Sanhedrin writes `~/.vestige/sanhedrin/latest.json` and `latest.html` with Claim -> Verdict -> Precedent -> Fix -> Appeal.
- **Dashboard Verdict Bar.** The dashboard shows PASS, NOTE, CAUTION, VETO, or APPEALED globally, expands into the receipt, and records stale/wrong/too-strict appeals.
- **Claim ledger.** Claim-mode Sanhedrin output now maps every extracted claim into structured JSON instead of treating the whole draft as one blob.
- **Appeal training.** Appeals are saved to `appeals.jsonl` and suppress future vetoes for the same claim fingerprint.
## What's New in v2.1.21 "Agent-Neutral Hardening"
v2.1.21 tightens Vestige for normal use across MCP-compatible agents, without
making Claude Code companion tooling part of the default path.
- **Agent-neutral default.** Stdio MCP remains the default transport; optional HTTP MCP is explicit with `--http`, `--http-port`, or `VESTIGE_HTTP_ENABLED=1`.
- **Safer destructive actions.** `memory(action="delete")` now requires `confirm=true`, matching `purge`, and the legacy `delete_knowledge` shim forwards that confirmation instead of bypassing it.
- **Portable sync repair.** Merge imports preserve purge tombstones, avoid `INSERT OR REPLACE` cascades, rebuild the vector index from a clean state, and write portable archive temp files with private Unix permissions.
- **Release/package cleanup.** Release builds check the embedded dashboard before packaging, publish checksums, and the npm installer rejects targets that do not have release assets.
- **Any-agent memory protocol.** The setup docs now include a short agent-agnostic memory protocol for Claude Code, Codex, Cursor, VS Code, Xcode, JetBrains, Windsurf, and other MCP clients.
## What's New in v2.1.2 "Honest Memory"
v2.1.2 makes Vestige easier to trust in everyday work: literal lookups stay literal, purge really removes content, contradictions are inspectable, and updates no longer require a curl reinstall flow.
- **Concrete search mode.** Quoted strings, env vars, UUIDs, paths, and code identifiers now take a keyword/literal path that skips HyDE, semantic fusion, FSRS reweighting, competition, and spreading activation. Exact things like `OPENAI_API_KEY`, `mlx_lm.server`, and migration IDs land first.
- **Irreversible purge.** `memory(action="purge", confirm=true)` permanently removes memory content and embeddings, scrubs insight JSON references, detaches temporal-summary children, prunes graph edges, and keeps only a non-content deletion tombstone for sync/audit.
- **First-class contradiction inspection.** New `contradictions` MCP tool surfaces trust-weighted disagreements directly instead of hiding them inside `deep_reference`.
- **Simple update flow.** `vestige update` refreshes binaries. Claude Code Cognitive Sandwich companion files are opt-in with `vestige update --sandwich-companion` or `vestige sandwich install`.
- **Pro waitlist preview.** `/dashboard/waitlist` adds a local-first Solo Pro and Team Pro early-access surface. `VITE_WAITLIST_ENDPOINT` and `VITE_SUPPORT_BOT_ENDPOINT` are opt-in dashboard env vars, so no signup data is captured unless endpoints are configured.
## What's New in v2.1.1 "Portable Sync"
v2.1.1 focuses on the biggest post-launch ask: move memories between machines without losing cognitive state. It also adds opt-in Qwen3 embeddings for higher-recall local retrieval.
- **Exact portable archives.** `vestige portable-export` / `vestige portable-import` preserve IDs, FSRS state, graph edges, suppression state, audit rows, and embedding blobs for Vestige-to-Vestige device transfer.
- **Sync-safe merge storage.** `vestige portable-import --merge` and `vestige sync <archive>` merge non-empty databases, apply delete tombstones, keep newer local memories, rebuild FTS, and push through a pluggable portable-sync backend. v2.1.1 ships the file backend for Dropbox, iCloud, Syncthing, Git, and shared folders.
- **Qwen3 embeddings.** Build with `qwen3-embeddings`, set `VESTIGE_EMBEDDING_MODEL=qwen3-0.6b`, and run `vestige consolidate` to re-embed existing memories. `vestige health` reports mixed-model stores before search quality is affected.
- **Model-aware retrieval.** Vestige now avoids comparing Qwen and Nomic vectors in the same search/dedup path.
## What's New in v2.1.0 "Cognitive Sandwich Goes Local"
v2.1.0 adds an opt-in Claude Code hook harness around the existing Vestige MCP server. The MCP tool surface and database schema stay backward compatible, while preflight hooks can inject trusted memory context before Claude answers. The heavyweight Sanhedrin verifier is optional and can be enabled separately.
- **Optional Sanhedrin Executioner.** The post-response verifier is off by default. Users can enable it with an OpenAI-compatible endpoint on x86/Linux/Intel Mac, or add `--with-launchd` on Apple Silicon to run the local MLX Qwen backend.
- **One-command Cognitive Sandwich installer.** `vestige sandwich install` stages hook files and agents by default, removes old Vestige hook wiring, and leaves all Claude Code hook layers plus the 19 GB model path opt-in.
- **Pulse hook backed by `/api/changelog`.** Fresh dream and connection events can be injected into the next Claude Code prompt context without blocking the prompt.
- **`VESTIGE_DATA_DIR` support.** `--data-dir` now has an env-var fallback, tilde expansion, secure directory creation, and clear precedence docs.
- **NPM release wrapper fixed.** `vestige-mcp-server@2.1.0` now downloads binaries from the matching `v2.1.0` GitHub release tag instead of an old hardcoded release.
## What's New in v2.0.9 "Autopilot"
Autopilot flips Vestige from passive memory library to **self-managing cognitive surface**. Same 24 MCP tools, zero schema changes — but the moment you upgrade, 14 previously dormant cognitive primitives start firing on live events without any tool call from your client.
- **One supervised backend task subscribes to the 20-event WebSocket bus** and routes six event classes into the cognitive engine: `MemoryCreated` triggers synaptic-tagging PRP + predictive-access records, `SearchPerformed` warms the speculative-retrieval model, `MemoryPromoted` fires activation spread, `MemorySuppressed` emits the Rac1 cascade wave, high-importance `ImportanceScored` (>0.85) auto-promotes, and `Heartbeat` rate-limit-fires `find_duplicates` on large DBs. **The engine mutex is never held across `.await`, so MCP dispatch is never starved.**
- **Panic-resilient supervisors.** Both background tasks run inside an outer supervisor loop — if one handler panics on a bad memory, the supervisor respawns it in 5 s instead of losing every future event.
- **Fully backward compatible.** No new MCP tools. No schema migration. Existing v2.0.8 databases open without a single step. Opt out with `VESTIGE_AUTOPILOT_ENABLED=0` if you want the passive-library contract back.
- **3,091 LOC of orphan v1.0 tool code removed** — nine superseded modules (`checkpoint`, `codebase`, `consolidate`, `ingest`, `intentions`, `knowledge`, `recall`, plus helpers) verified zero non-test callers before deletion. Tool surface unchanged.
## What's New in v2.0.8 "Pulse"
v2.0.8 wires the dashboard through to the cognitive engine. Eight new surfaces expose the reasoning stack visually — every one was MCP-only before.
- **Reasoning Theater (`/reasoning`)**`Cmd+K` Ask palette over the 8-stage `deep_reference` pipeline (hybrid retrieval → cross-encoder rerank → spreading activation → FSRS-6 trust → temporal supersession → contradiction analysis → relation assessment → template reasoning chain). Evidence cards, confidence meter, contradiction geodesic arcs, superseded-memory lineage, evolution timeline. **Zero LLM calls, 100% local.**
- **Pulse InsightToast** — real-time toasts for `DreamCompleted`, `ConsolidationCompleted`, `ConnectionDiscovered`, promote/demote/suppress/unsuppress, `Rac1CascadeSwept`. Rate-limited, auto-dismiss, click-to-dismiss.
- **Memory Birth Ritual (Terrarium)** — new memories materialize in the 3D graph on every `MemoryCreated`: elastic scale-in, quadratic Bezier flight path, glow sprite fade-in, Newton's Cradle docking recoil. 60-frame sequence, zero-alloc math.
- **7 more dashboard surfaces**`/duplicates`, `/dreams`, `/schedule`, `/importance`, `/activation`, `/contradictions`, `/patterns`. Left nav expanded 8 → 16 with single-key shortcuts.
- **Intel Mac (`x86_64-apple-darwin`) support restored** via the `ort-dynamic` Cargo feature + Homebrew `onnxruntime`. Microsoft deprecated x86_64 macOS prebuilts; the dynamic-link path sidesteps that permanently. **Closes #41.**
- **Contradiction-detection false positives eliminated** — four thresholds tightened so adjacent-domain memories no longer flag as conflicts. On an FSRS-6 query this collapses false contradictions 12 → 0 without regressing legitimate test cases.
## What's New in v2.0.7 "Visible"
Hygiene release closing two UI gaps and finishing schema cleanup. No breaking changes, no user-data migrations.
- **`POST /api/memories/{id}/suppress` + `/unsuppress` HTTP endpoints** — dashboard can trigger Anderson 2025 SIF + Rac1 cascade without dropping to raw MCP. `suppressionCount`, `retrievalPenalty`, `reversibleUntil`, `labileWindowHours` all in response. Suppress button joins Promote / Demote / Delete on the Memories page.
- **Uptime in the sidebar footer** — the `Heartbeat` event has carried `uptime_secs` since v2.0.5 but was never rendered. Now shows as `up 3d 4h` / `up 18m` / `up 47s`.
- **`execute_export` panic fix** — unreachable match arm replaced with a clean "unsupported export format" error instead of unwinding through the MCP dispatcher.
- **`predict` surfaces `predict_degraded: true`** on lock poisoning instead of silently returning empty vecs. `memory_changelog` honors `start` / `end` bounds. `intention` check honors `include_snoozed`.
- **Migration V11** — drops dead `knowledge_edges` + `compressed_memories` tables (added speculatively in V4, never used).
## What's New in v2.0.6 "Composer"
v2.0.6 is a polish release that makes the existing cognitive stack finally *feel* alive in the dashboard and stays out of your way on the prompt side.
- **Six live graph reactions, not one**`MemorySuppressed`, `MemoryUnsuppressed`, `Rac1CascadeSwept`, `Connected`, `ConsolidationStarted`, and `ImportanceScored` now light the 3D graph in real time. v2.0.5 shipped `suppress` but the graph was silent when you called it; consolidation and importance scoring have been silent since v2.0.0. No longer.
- **Intentions page actually works** — fixes a long-standing bug where every intention rendered as "normal priority" (type/schema drift between backend and frontend) and context/time triggers surfaced as raw JSON.
- **Opt-in composition mandate** — the new MCP `instructions` string stays minimal by default. Opt in to the full Composing / Never-composed / Recommendation composition protocol with `VESTIGE_SYSTEM_PROMPT_MODE=full` when you want it, and nothing is imposed on your sessions when you don't.
## What's New in v2.0.5 "Intentional Amnesia"
**The first shipped AI memory system with top-down inhibitory control over retrieval.** Other systems implement passive decay — memories fade if you don't touch them. Vestige v2.0.5 also implements *active* suppression: the new **`suppress`** tool compounds a retrieval penalty on every call (up to 80%), a background Rac1 worker fades co-activated neighbors over 72 hours, and the whole thing is reversible within a 24-hour labile window. **Never deletes.** The memory is inhibited, not erased.
Ebbinghaus 1885 models what happens to memories you don't touch. Anderson 2025 models what happens when you actively want to stop thinking about one. Every other AI memory system implements the first. Vestige is the first to ship the second.
Based on [Anderson et al. 2025](https://www.nature.com/articles/s41583-025-00929-y) (Suppression-Induced Forgetting, *Nat Rev Neurosci*) and [Cervantes-Sandoval et al. 2020](https://pmc.ncbi.nlm.nih.gov/articles/PMC7477079/) (Rac1 synaptic cascade).
<details>
<summary>Earlier releases (v2.0 "Cognitive Leap" → v2.0.4 "Deep Reference")</summary>
- **v2.0.4 — `deep_reference` Tool** — 8-stage cognitive reasoning pipeline with FSRS-6 trust scoring, intent classification, spreading activation, contradiction analysis, and pre-built reasoning chains. Token budgets raised 10K → 100K. CORS tightened.
- **v2.0 — 3D Memory Dashboard** — SvelteKit + Three.js neural visualization with real-time WebSocket events, bloom post-processing, force-directed graph layout.
- **v2.0 — WebSocket Event Bus** — Every cognitive operation broadcasts events: memory creation, search, dreaming, consolidation, retention decay.
- **v2.0 — HyDE Query Expansion** — Template-based Hypothetical Document Embeddings for dramatically improved search quality on conceptual queries.
- **v2.0 — Nomic v2 MoE (experimental)** — fastembed 5.11 with optional Nomic Embed Text v2 MoE (475M params, 8 experts) + Metal GPU acceleration.
- **v2.0 — Command Palette**`Cmd+K` navigation, keyboard shortcuts, responsive mobile layout, PWA installable.
- **v2.0 — FSRS Decay Visualization** — SVG retention curves with predicted decay at 1d/7d/30d.
</details>
- **Model-agnostic Sanhedrin presets.** Sanhedrin no longer guesses a large default verifier. Users choose any OpenAI-compatible endpoint/model, or start from custom, small laptop, Ollama, MLX, vLLM, llama.cpp, hosted API, or LiteLLM presets.
- **Sharper Receipt Lock.** Verification claims inside code fences, quotes, blockquotes, or explicitly hedged "let me verify" language no longer trigger false vetoes, while actual "tests passed" claims still require command receipts.
- **Safer command receipts.** Transcript command evidence now prefers structured tool-use receipts; loose JSON scanning is opt-in only.
- **Visible fail-open telemetry.** Timeouts, unavailable model endpoints, and malformed verdicts are logged locally and surfaced in the dashboard's 7-day Sanhedrin stats.
- **Durable evidence boundary.** Staged evidence remains useful context, but it cannot satisfy durable support or contradiction requirements by itself.
- **Safer batch writes.** `smart_ingest` batch mode now keeps caller-separated items separate by default and returns merge previews when an existing memory is mutated.
- **Opt-in NVIDIA acceleration path.** Qwen3 embedding builds expose CUDA/cuDNN feature flags for contributors and users with CUDA-capable hosts.
---
@ -444,6 +345,53 @@ cargo build --release -p vestige-mcp --features qwen3-embeddings,metal
VESTIGE_EMBEDDING_MODEL=qwen3-0.6b vestige consolidate
```
### Building with CUDA support (NVIDIA hosts - Windows / Linux)
The `cuda` feature routes Qwen3 embedding through NVIDIA GPUs via
`candle-core/cuda`. On a host with the CUDA toolkit installed and a supported
NVIDIA runtime, this drops Qwen3-Embedding inference from CPU-bound to GPU-bound
for batched workloads.
```bash
# Linux / Windows + CUDA toolkit (12.x or 13.x)
cargo build --release -p vestige-mcp --features qwen3-embeddings,cuda
# Optional cuDNN acceleration on top of CUDA
cargo build --release -p vestige-mcp --features qwen3-embeddings,cudnn
VESTIGE_EMBEDDING_MODEL=qwen3-0.6b vestige consolidate
```
**Prerequisites:**
- NVIDIA driver + CUDA toolkit (12.x or 13.x). Verify with `nvcc --version`.
- A C++ host compiler that `nvcc` can drive (Linux: `gcc`; Windows: MSVC /
`cl.exe` from a recent Visual Studio Build Tools install).
**Windows + MSVC + CUDA 13.x build note.** Recent CCCL headers shipped with
CUDA 13.x require the modern preprocessor. Without it, the `candle-kernels`
`.cu` compile pass can fail at `cuda/include/cuda/std/__cccl/compiler.h`. Set
this env var before `cargo build` to pass `/Zc:preprocessor` through `nvcc`:
```powershell
# PowerShell
$env:NVCC_PREPEND_FLAGS = '-Xcompiler="/Zc:preprocessor"'
cargo build --release -p vestige-mcp --features qwen3-embeddings,cuda
```
```cmd
:: cmd.exe
set NVCC_PREPEND_FLAGS=-Xcompiler="/Zc:preprocessor"
cargo build --release -p vestige-mcp --features qwen3-embeddings,cuda
```
Linux + CUDA 13.x builds with `gcc` do not need the equivalent flag.
**Verifying GPU is actually used.** With CUDA-enabled builds, run
`VESTIGE_EMBEDDING_MODEL=qwen3-0.6b vestige consolidate` on a corpus of 1000+
memories and watch `nvidia-smi`; embedding passes should pin a single GPU while
the run is active.
---
## CLI

View file

@ -1 +1 @@
const r="/api";async function t(e,o){const i=await fetch(`${r}${e}`,{headers:{"Content-Type":"application/json"},...o});if(!i.ok)throw new Error(`API ${i.status}: ${i.statusText}`);return i.json()}const n={memories:{list:e=>{const o=e?"?"+new URLSearchParams(e).toString():"";return t(`/memories${o}`)},get:e=>t(`/memories/${e}`),delete:e=>t(`/memories/${e}`,{method:"DELETE"}),promote:e=>t(`/memories/${e}/promote`,{method:"POST"}),demote:e=>t(`/memories/${e}/demote`,{method:"POST"}),suppress:(e,o)=>t(`/memories/${e}/suppress`,{method:"POST",body:o?JSON.stringify({reason:o}):void 0}),unsuppress:e=>t(`/memories/${e}/unsuppress`,{method:"POST"})},search:(e,o=20)=>t(`/search?q=${encodeURIComponent(e)}&limit=${o}`),stats:()=>t("/stats"),health:()=>t("/health"),timeline:(e=7,o=200)=>t(`/timeline?days=${e}&limit=${o}`),graph:e=>{const o=e?"?"+new URLSearchParams(Object.entries(e).filter(([,i])=>i!==void 0).map(([i,s])=>[i,String(s)])).toString():"";return t(`/graph${o}`)},dream:()=>t("/dream",{method:"POST"}),explore:(e,o="associations",i,s=10)=>t("/explore",{method:"POST",body:JSON.stringify({from_id:e,action:o,to_id:i,limit:s})}),predict:()=>t("/predict",{method:"POST"}),importance:e=>t("/importance",{method:"POST",body:JSON.stringify({content:e})}),consolidate:()=>t("/consolidate",{method:"POST"}),retentionDistribution:()=>t("/retention-distribution"),intentions:(e="active")=>t(`/intentions?status=${e}`),deepReference:(e,o=20)=>t("/deep_reference",{method:"POST",body:JSON.stringify({query:e,depth:o})}),sanhedrin:{latest:()=>t("/sanhedrin/latest"),appeal:(e,o,i,s)=>t("/sanhedrin/appeal",{method:"POST",body:JSON.stringify({reason:e,note:o,claimId:i,receiptId:s})})}};export{n as a};
const r="/api";async function t(e,o){const i=await fetch(`${r}${e}`,{headers:{"Content-Type":"application/json"},...o});if(!i.ok)throw new Error(`API ${i.status}: ${i.statusText}`);return i.json()}const n={memories:{list:e=>{const o=e?"?"+new URLSearchParams(e).toString():"";return t(`/memories${o}`)},get:e=>t(`/memories/${e}`),delete:e=>t(`/memories/${e}`,{method:"DELETE"}),promote:e=>t(`/memories/${e}/promote`,{method:"POST"}),demote:e=>t(`/memories/${e}/demote`,{method:"POST"}),suppress:(e,o)=>t(`/memories/${e}/suppress`,{method:"POST",body:o?JSON.stringify({reason:o}):void 0}),unsuppress:e=>t(`/memories/${e}/unsuppress`,{method:"POST"})},search:(e,o=20)=>t(`/search?q=${encodeURIComponent(e)}&limit=${o}`),stats:()=>t("/stats"),health:()=>t("/health"),timeline:(e=7,o=200)=>t(`/timeline?days=${e}&limit=${o}`),graph:e=>{const o=e?"?"+new URLSearchParams(Object.entries(e).filter(([,i])=>i!==void 0).map(([i,s])=>[i,String(s)])).toString():"";return t(`/graph${o}`)},dream:()=>t("/dream",{method:"POST"}),explore:(e,o="associations",i,s=10)=>t("/explore",{method:"POST",body:JSON.stringify({from_id:e,action:o,to_id:i,limit:s})}),predict:()=>t("/predict",{method:"POST"}),importance:e=>t("/importance",{method:"POST",body:JSON.stringify({content:e})}),consolidate:()=>t("/consolidate",{method:"POST"}),retentionDistribution:()=>t("/retention-distribution"),intentions:(e="active")=>t(`/intentions?status=${e}`),deepReference:(e,o=20)=>t("/deep_reference",{method:"POST",body:JSON.stringify({query:e,depth:o})}),sanhedrin:{latest:()=>t("/sanhedrin/latest"),telemetry:(e=7)=>t(`/sanhedrin/telemetry?days=${e}`),appeal:(e,o,i,s)=>t("/sanhedrin/appeal",{method:"POST",body:JSON.stringify({reason:e,note:o,claimId:i,receiptId:s})})}};export{n as a};

View file

@ -1 +0,0 @@
import{a as r}from"../chunks/D-gDZzN6.js";import{w as t}from"../chunks/DGcYlAAw.js";export{t as load_css,r as start};

View file

@ -0,0 +1 @@
import{a as r}from"../chunks/C2TQQEIa.js";import{w as t}from"../chunks/D8UfWY0j.js";export{t as load_css,r as start};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
import"../chunks/Bzak7iHL.js";import{i as h}from"../chunks/BUoSzNdg.js";import{p as g,f as d,t as l,a as v,d as s,r as o,e as _}from"../chunks/CpWkWWOo.js";import{s as p}from"../chunks/BlVfL1ME.js";import{a as x,f as $}from"../chunks/CHOnp4oo.js";import{p as m}from"../chunks/DGcYlAAw.js";import{s as k}from"../chunks/D-gDZzN6.js";const b={get error(){return m.error},get status(){return m.status}};k.updated.check;const i=b;var E=$("<h1> </h1> <p> </p>",1);function C(f,n){g(n,!1),h();var t=E(),r=d(t),c=s(r,!0);o(r);var a=_(r,2),u=s(a,!0);o(a),l(()=>{var e;p(c,i.status),p(u,(e=i.error)==null?void 0:e.message)}),x(f,t),v()}export{C as component};
import"../chunks/Bzak7iHL.js";import{i as h}from"../chunks/BUoSzNdg.js";import{p as g,f as d,t as l,a as v,d as s,r as o,e as _}from"../chunks/CpWkWWOo.js";import{s as p}from"../chunks/BlVfL1ME.js";import{a as x,f as $}from"../chunks/CHOnp4oo.js";import{p as m}from"../chunks/D8UfWY0j.js";import{s as k}from"../chunks/C2TQQEIa.js";const b={get error(){return m.error},get status(){return m.status}};k.updated.check;const i=b;var E=$("<h1> </h1> <p> </p>",1);function C(f,n){g(n,!1),h();var t=E(),r=d(t),c=s(r,!0);o(r);var a=_(r,2),u=s(a,!0);o(a),l(()=>{var e;p(c,i.status),p(u,(e=i.error)==null?void 0:e.message)}),x(f,t),v()}export{C as component};

View file

@ -1,4 +1,4 @@
var Bc=Object.defineProperty;var zc=(i,t,e)=>t in i?Bc(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e;var kt=(i,t,e)=>zc(i,typeof t!="symbol"?t+"":t,e);import"../chunks/Bzak7iHL.js";import{o as jl,a as Zl}from"../chunks/GG5zm9kr.js";import{s as me,c as va,h as zt,g as B,p as ys,aB as kc,a as Es,d as yt,e as bt,n as Hc,r as xt,t as Ke,u as Gn,f as Kl,j as Vc}from"../chunks/CpWkWWOo.js";import{s as fe,d as $l,a as Fe}from"../chunks/BlVfL1ME.js";import{i as kn}from"../chunks/B4yTwGkE.js";import{e as _s,i as hr}from"../chunks/CGEBXrjl.js";import{a as _e,f as Se,c as Gc}from"../chunks/CHOnp4oo.js";import{s as ve,r as xa}from"../chunks/A7po6GxK.js";import{s as Us}from"../chunks/aVbAZ-t7.js";import{s as Sr}from"../chunks/Cx-f-Pzo.js";import{b as Ma}from"../chunks/sZcqyNBA.js";import{b as Jl}from"../chunks/BnXDGOmJ.js";import{s as Wc,a as Xc}from"../chunks/C6HuKgyx.js";import{b as Do}from"../chunks/DGcYlAAw.js";import{b as Yc}from"../chunks/CJsMJEun.js";import{p as vs}from"../chunks/V6gjw5Ec.js";import{N as Sa}from"../chunks/CcUbQ_Wl.js";import{i as qc}from"../chunks/BUoSzNdg.js";import{a as gi}from"../chunks/554JRhq6.js";import{e as jc}from"../chunks/MAY1QfFZ.js";/**
var Bc=Object.defineProperty;var zc=(i,t,e)=>t in i?Bc(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e;var kt=(i,t,e)=>zc(i,typeof t!="symbol"?t+"":t,e);import"../chunks/Bzak7iHL.js";import{o as jl,a as Zl}from"../chunks/GG5zm9kr.js";import{s as me,c as va,h as zt,g as B,p as ys,aB as kc,a as Es,d as yt,e as bt,n as Hc,r as xt,t as Ke,u as Gn,f as Kl,j as Vc}from"../chunks/CpWkWWOo.js";import{s as fe,d as $l,a as Fe}from"../chunks/BlVfL1ME.js";import{i as kn}from"../chunks/B4yTwGkE.js";import{e as _s,i as hr}from"../chunks/CGEBXrjl.js";import{a as _e,f as Se,c as Gc}from"../chunks/CHOnp4oo.js";import{s as ve,r as xa}from"../chunks/A7po6GxK.js";import{s as Us}from"../chunks/aVbAZ-t7.js";import{s as Sr}from"../chunks/Cx-f-Pzo.js";import{b as Ma}from"../chunks/sZcqyNBA.js";import{b as Jl}from"../chunks/BnXDGOmJ.js";import{s as Wc,a as Xc}from"../chunks/C6HuKgyx.js";import{b as Do}from"../chunks/D8UfWY0j.js";import{b as Yc}from"../chunks/CJsMJEun.js";import{p as vs}from"../chunks/V6gjw5Ec.js";import{N as Sa}from"../chunks/CcUbQ_Wl.js";import{i as qc}from"../chunks/BUoSzNdg.js";import{a as gi}from"../chunks/B7CfdQuM.js";import{e as jc}from"../chunks/MAY1QfFZ.js";/**
* @license
* Copyright 2010-2024 Three.js Authors
* SPDX-License-Identifier: MIT

View file

@ -1 +1 @@
import"../chunks/Bzak7iHL.js";import{o as pe}from"../chunks/GG5zm9kr.js";import{p as ce,s as b,c as me,g as e,a as _e,e as i,d as a,h as c,r as t,t as g}from"../chunks/CpWkWWOo.js";import{d as ue,a as K,s as m}from"../chunks/BlVfL1ME.js";import{i as M}from"../chunks/B4yTwGkE.js";import{e as h,i as P}from"../chunks/CGEBXrjl.js";import{a as l,f as v}from"../chunks/CHOnp4oo.js";import{s as Q}from"../chunks/Cx-f-Pzo.js";import{b as xe}from"../chunks/BnXDGOmJ.js";import{a as fe}from"../chunks/554JRhq6.js";import{N as U}from"../chunks/CcUbQ_Wl.js";var be=v('<div class="h-16 glass-subtle rounded-xl animate-pulse"></div>'),ge=v('<div class="space-y-4"></div>'),he=v('<div class="text-center py-20 text-dim"><p>No memories in the selected time range.</p></div>'),ye=v('<div class="w-2 h-2 rounded-full"></div>'),we=v('<span class="text-xs text-muted"> </span>'),ke=v('<div class="flex items-start gap-2 text-sm"><div class="w-2 h-2 mt-1.5 rounded-full flex-shrink-0"></div> <div class="flex-1 min-w-0"><span class="text-dim line-clamp-1"> </span></div> <span class="text-xs text-muted flex-shrink-0"> </span></div>'),Te=v('<div class="mt-3 pt-3 border-t border-synapse/10 space-y-2"></div>'),je=v('<div class="relative pl-14"><div class="absolute left-4 top-3 w-5 h-5 rounded-full border-2 border-synapse bg-void flex items-center justify-center"><div class="w-2 h-2 rounded-full bg-synapse"></div></div> <button class="w-full text-left p-4 glass-subtle rounded-xl hover:bg-white/[0.03] transition-all"><div class="flex items-center justify-between"><div><span class="text-sm text-bright font-medium"> </span> <span class="text-xs text-dim ml-2"> </span></div> <div class="flex gap-1"><!> <!></div></div> <!></button></div>'),Ae=v('<div class="relative"><div class="absolute left-6 top-0 bottom-0 w-px bg-synapse/15"></div> <div class="space-y-4"></div></div>'),Ne=v('<div class="p-6 max-w-4xl mx-auto space-y-6"><div class="flex items-center justify-between"><h1 class="text-xl text-bright font-semibold">Timeline</h1> <select class="px-3 py-2 bg-white/[0.03] border border-synapse/10 rounded-xl text-dim text-sm focus:outline-none backdrop-blur-sm"><option>7 days</option><option>14 days</option><option>30 days</option><option>90 days</option></select></div> <!></div>');function Re(V,W){ce(W,!0);let _=b(me([])),y=b(!0),w=b(14),k=b(null);pe(()=>R());async function R(){c(y,!0);try{const s=await fe.timeline(e(w),500);c(_,s.timeline,!0)}catch{c(_,[],!0)}finally{c(y,!1)}}var T=Ne(),j=a(T),u=i(a(j),2),A=a(u);A.value=A.__value=7;var N=i(A);N.value=N.__value=14;var O=i(N);O.value=O.__value=30;var Y=i(O);Y.value=Y.__value=90,t(u),t(j);var X=i(j,2);{var Z=s=>{var d=ge();h(d,20,()=>Array(7),P,(x,f)=>{var r=be();l(x,r)}),t(d),l(s,d)},ee=s=>{var d=he();l(s,d)},te=s=>{var d=Ae(),x=i(a(d),2);h(x,21,()=>e(_),f=>f.date,(f,r)=>{var S=je(),B=i(a(S),2),D=a(B),E=a(D),$=a(E),ae=a($,!0);t($);var q=i($,2),se=a(q);t(q),t(E);var z=i(E,2),G=a(z);h(G,17,()=>e(r).memories.slice(0,10),P,(n,o)=>{var p=ye();g(()=>Q(p,`background: ${(U[e(o).nodeType]||"#8B95A5")??""}; opacity: ${.3+e(o).retentionStrength*.7}`)),l(n,p)});var ie=i(G,2);{var re=n=>{var o=we(),p=a(o);t(o),g(()=>m(p,`+${e(r).memories.length-10}`)),l(n,o)};M(ie,n=>{e(r).memories.length>10&&n(re)})}t(z),t(D);var oe=i(D,2);{var le=n=>{var o=Te();h(o,21,()=>e(r).memories,P,(p,C)=>{var F=ke(),H=a(F),L=i(H,2),I=a(L),ve=a(I,!0);t(I),t(L);var J=i(L,2),de=a(J);t(J),t(F),g(ne=>{Q(H,`background: ${(U[e(C).nodeType]||"#8B95A5")??""}`),m(ve,e(C).content),m(de,`${ne??""}%`)},[()=>(e(C).retentionStrength*100).toFixed(0)]),l(p,F)}),t(o),l(n,o)};M(oe,n=>{e(k)===e(r).date&&n(le)})}t(B),t(S),g(()=>{m(ae,e(r).date),m(se,`${e(r).count??""} memories`)}),K("click",B,()=>c(k,e(k)===e(r).date?null:e(r).date,!0)),l(f,S)}),t(x),t(d),l(s,d)};M(X,s=>{e(y)?s(Z):e(_).length===0?s(ee,1):s(te,!1)})}t(T),K("change",u,R),xe(u,()=>e(w),s=>c(w,s)),l(V,T),_e()}ue(["change","click"]);export{Re as component};
import"../chunks/Bzak7iHL.js";import{o as pe}from"../chunks/GG5zm9kr.js";import{p as ce,s as b,c as me,g as e,a as _e,e as i,d as a,h as c,r as t,t as g}from"../chunks/CpWkWWOo.js";import{d as ue,a as K,s as m}from"../chunks/BlVfL1ME.js";import{i as M}from"../chunks/B4yTwGkE.js";import{e as h,i as P}from"../chunks/CGEBXrjl.js";import{a as l,f as v}from"../chunks/CHOnp4oo.js";import{s as Q}from"../chunks/Cx-f-Pzo.js";import{b as xe}from"../chunks/BnXDGOmJ.js";import{a as fe}from"../chunks/B7CfdQuM.js";import{N as U}from"../chunks/CcUbQ_Wl.js";var be=v('<div class="h-16 glass-subtle rounded-xl animate-pulse"></div>'),ge=v('<div class="space-y-4"></div>'),he=v('<div class="text-center py-20 text-dim"><p>No memories in the selected time range.</p></div>'),ye=v('<div class="w-2 h-2 rounded-full"></div>'),we=v('<span class="text-xs text-muted"> </span>'),ke=v('<div class="flex items-start gap-2 text-sm"><div class="w-2 h-2 mt-1.5 rounded-full flex-shrink-0"></div> <div class="flex-1 min-w-0"><span class="text-dim line-clamp-1"> </span></div> <span class="text-xs text-muted flex-shrink-0"> </span></div>'),Te=v('<div class="mt-3 pt-3 border-t border-synapse/10 space-y-2"></div>'),je=v('<div class="relative pl-14"><div class="absolute left-4 top-3 w-5 h-5 rounded-full border-2 border-synapse bg-void flex items-center justify-center"><div class="w-2 h-2 rounded-full bg-synapse"></div></div> <button class="w-full text-left p-4 glass-subtle rounded-xl hover:bg-white/[0.03] transition-all"><div class="flex items-center justify-between"><div><span class="text-sm text-bright font-medium"> </span> <span class="text-xs text-dim ml-2"> </span></div> <div class="flex gap-1"><!> <!></div></div> <!></button></div>'),Ae=v('<div class="relative"><div class="absolute left-6 top-0 bottom-0 w-px bg-synapse/15"></div> <div class="space-y-4"></div></div>'),Ne=v('<div class="p-6 max-w-4xl mx-auto space-y-6"><div class="flex items-center justify-between"><h1 class="text-xl text-bright font-semibold">Timeline</h1> <select class="px-3 py-2 bg-white/[0.03] border border-synapse/10 rounded-xl text-dim text-sm focus:outline-none backdrop-blur-sm"><option>7 days</option><option>14 days</option><option>30 days</option><option>90 days</option></select></div> <!></div>');function Re(V,W){ce(W,!0);let _=b(me([])),y=b(!0),w=b(14),k=b(null);pe(()=>R());async function R(){c(y,!0);try{const s=await fe.timeline(e(w),500);c(_,s.timeline,!0)}catch{c(_,[],!0)}finally{c(y,!1)}}var T=Ne(),j=a(T),u=i(a(j),2),A=a(u);A.value=A.__value=7;var N=i(A);N.value=N.__value=14;var O=i(N);O.value=O.__value=30;var Y=i(O);Y.value=Y.__value=90,t(u),t(j);var X=i(j,2);{var Z=s=>{var d=ge();h(d,20,()=>Array(7),P,(x,f)=>{var r=be();l(x,r)}),t(d),l(s,d)},ee=s=>{var d=he();l(s,d)},te=s=>{var d=Ae(),x=i(a(d),2);h(x,21,()=>e(_),f=>f.date,(f,r)=>{var S=je(),B=i(a(S),2),D=a(B),E=a(D),$=a(E),ae=a($,!0);t($);var q=i($,2),se=a(q);t(q),t(E);var z=i(E,2),G=a(z);h(G,17,()=>e(r).memories.slice(0,10),P,(n,o)=>{var p=ye();g(()=>Q(p,`background: ${(U[e(o).nodeType]||"#8B95A5")??""}; opacity: ${.3+e(o).retentionStrength*.7}`)),l(n,p)});var ie=i(G,2);{var re=n=>{var o=we(),p=a(o);t(o),g(()=>m(p,`+${e(r).memories.length-10}`)),l(n,o)};M(ie,n=>{e(r).memories.length>10&&n(re)})}t(z),t(D);var oe=i(D,2);{var le=n=>{var o=Te();h(o,21,()=>e(r).memories,P,(p,C)=>{var F=ke(),H=a(F),L=i(H,2),I=a(L),ve=a(I,!0);t(I),t(L);var J=i(L,2),de=a(J);t(J),t(F),g(ne=>{Q(H,`background: ${(U[e(C).nodeType]||"#8B95A5")??""}`),m(ve,e(C).content),m(de,`${ne??""}%`)},[()=>(e(C).retentionStrength*100).toFixed(0)]),l(p,F)}),t(o),l(n,o)};M(oe,n=>{e(k)===e(r).date&&n(le)})}t(B),t(S),g(()=>{m(ae,e(r).date),m(se,`${e(r).count??""} memories`)}),K("click",B,()=>c(k,e(k)===e(r).date?null:e(r).date,!0)),l(f,S)}),t(x),t(d),l(s,d)};M(X,s=>{e(y)?s(Z):e(_).length===0?s(ee,1):s(te,!1)})}t(T),K("change",u,R),xe(u,()=>e(w),s=>c(w,s)),l(V,T),_e()}ue(["change","click"]);export{Re as component};

View file

@ -1 +1 @@
import"../chunks/Bzak7iHL.js";import{i as p}from"../chunks/BUoSzNdg.js";import{o as r}from"../chunks/GG5zm9kr.js";import{p as t,a}from"../chunks/CpWkWWOo.js";import{g as m}from"../chunks/D-gDZzN6.js";function g(i,o){t(o,!1),r(()=>m("/graph",{replaceState:!0})),p(),a()}export{g as component};
import"../chunks/Bzak7iHL.js";import{i as p}from"../chunks/BUoSzNdg.js";import{o as r}from"../chunks/GG5zm9kr.js";import{p as t,a}from"../chunks/CpWkWWOo.js";import{g as m}from"../chunks/C2TQQEIa.js";function g(i,o){t(o,!1),r(()=>m("/graph",{replaceState:!0})),p(),a()}export{g as component};

Some files were not shown because too many files have changed in this diff Show more