Compare commits

..

1 commit

Author SHA1 Message Date
Sam Valladares
9c96838886 Release v2.1.22 Sanhedrin receipts 2026-05-25 01:12:38 -05:00
682 changed files with 7034 additions and 71825 deletions

View file

@ -17,15 +17,6 @@ jobs:
strategy:
fail-fast: false
matrix:
# Pin macOS to macos-14 (Sonoma): the rolling `macos-latest` image
# The "ld: library 'clang_rt.osx' not found" failure is NOT a flaky
# image. It is the linker resolving a clang compiler-rt search path
# (Xcode .../clang/<N>/lib/darwin) that no longer exists on the runner,
# because a STALE `target/` was restored from the cache after the
# runner image's default Xcode moved. The real fix is the cache change
# below (we no longer cache `target/`, and the key is bumped to v2 to
# discard the poisoned caches), so the runner choice is no longer
# load-bearing. macos-latest is fine again with a clean target dir.
os: [macos-latest, ubuntu-latest]
steps:
- uses: actions/checkout@v4
@ -35,20 +26,17 @@ jobs:
with:
components: clippy
- name: Cache cargo (registry only)
- name: Cache cargo
uses: actions/cache@v4
with:
# Deliberately NOT caching `target/`: a `target/` built against a
# different Xcode carries a stale clang compiler-rt search path that
# breaks the linker ("clang_rt.osx not found"). Cache only the
# download-heavy registry/git dirs; recompiling is cheap and correct.
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: ${{ runner.os }}-cargo-v2-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-v2-
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-
- name: Check
run: cargo check --workspace
@ -73,13 +61,13 @@ jobs:
fail-fast: false
matrix:
include:
- os: macos-14
- os: macos-latest
target: aarch64-apple-darwin
cargo_flags: ""
# Intel Mac builds against a system ONNX Runtime via ort-dynamic
# (ort-sys has no x86_64-apple-darwin prebuilts). Compile-only here;
# runtime linking is a user concern documented in INSTALL-INTEL-MAC.md.
- os: macos-14
- os: macos-latest
target: x86_64-apple-darwin
cargo_flags: "--no-default-features --features ort-dynamic,vector-search"
- os: ubuntu-latest

View file

@ -1,25 +0,0 @@
name: Guard — No Private Cloud Code
# Fails if private Vestige Cloud *service* code (billing, sync-key/namespace
# mapping, Lemon Squeezy webhooks, transactional email) ever lands in this
# public repo. The public cloud *client* is allowed and does not trip this.
on:
push:
branches: [main, feat/cloud-sync-mvp]
pull_request:
workflow_dispatch:
permissions:
contents: read
jobs:
guard:
name: No private cloud service code
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Scan for private cloud service markers
run: ./scripts/check-no-private-cloud.sh

View file

@ -1,67 +0,0 @@
name: Deploy GitHub Pages
on:
push:
branches: [main, feat/cloud-sync-mvp]
paths:
- 'apps/dashboard/**'
- '.github/workflows/pages.yml'
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
# Allow one concurrent deployment; let an in-progress run finish.
concurrency:
group: pages
cancel-in-progress: false
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
env:
# GitHub Pages serves this project repo from the /vestige/ subpath.
# The dashboard must be built with a matching base so every _app/ asset
# resolves instead of 404ing.
VESTIGE_BASE_PATH: /vestige
steps:
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v5
with:
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build dashboard
run: pnpm --filter @vestige/dashboard build
- name: Assemble site root
run: |
# The Pages project site is already served from /vestige/, and the
# dashboard is built with base path /vestige (VESTIGE_BASE_PATH), so
# its files go directly at the artifact root — the base path already
# accounts for the subpath. No extra subdirectory or redirect needed.
rm -rf _site
mkdir -p _site
cp -r apps/dashboard/build/* _site/
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: _site
- id: deployment
uses: actions/deploy-pages@v4

View file

@ -31,14 +31,7 @@ jobs:
- target: x86_64-pc-windows-msvc
os: windows-latest
archive: zip
# vector-search (usearch HNSW index) is REQUIRED. Without it the
# storage layer's #[cfg(feature = "vector-search")] paths compile
# out, so embeddings are never persisted or queried: the binary
# reports "Embedding Service: Not Ready", 0% coverage, consolidate
# generates 0 embeddings, and no model download is ever attempted.
# usearch builds cleanly on MSVC because vestige-core pins it with
# features=["fp16lib"] (see crates/vestige-core/Cargo.toml).
cargo_flags: "--no-default-features --features embeddings,ort-download,vector-search"
cargo_flags: ""
needs_onnxruntime: false
# Intel Mac uses the ort-dynamic feature to runtime-link against a
# system libonnxruntime (Homebrew), sidestepping the missing
@ -58,7 +51,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event_name == 'workflow_dispatch' && github.sha || github.event.inputs.tag || github.ref }}
ref: ${{ github.event.inputs.tag || github.ref }}
- name: Setup pnpm
uses: pnpm/action-setup@v4

3
.gitignore vendored
View file

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

View file

@ -7,330 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Fixed — Auto-consolidation merge: opt-out lever + protected pins honored (#142)
The background consolidation cycle's auto-dedup pass silently concat-merges
near-duplicate memories (cosine ≥ 0.85): it keeps the strongest node, folds the
weaker ones in as `[MERGED]` blocks, and **hard-deletes** the originals — with no
reflog and no way to turn it off. Two fixes. First, it is now disableable: set
`VESTIGE_AUTO_CONSOLIDATE_MERGE=0` (or `false`/`off`/`no`) to suppress it. It
remains **on by default** (behavior unchanged), and the `dedup` MCP tool stays
available for on-demand, previewable, reversible merges regardless. Second,
**protected (pinned) memories are now excluded from this pass** — previously
`dedup protect` did nothing here, so a pinned memory could be absorbed or deleted
unattended, contradicting the interactive contract that a protected node may only
survive a merge, never be absorbed. A protected node is now never an anchor, never
a cluster member, and thus never merged into or deleted, whether the lever is on
or off.
## [2.2.1] - 2026-07-02 — "Windows embeddings + backfill safety"
A focused patch release. Two fixes plus a first-run guide.
### Fixed — Windows embeddings never initialized (#101)
The `x86_64-pc-windows-msvc` v2.2.0 binary was built without the `vector-search`
feature, so the storage layer's `#[cfg(feature = "vector-search")]` paths compiled
out. On Windows this meant new memories got no embedding, semantic search returned
nothing, `vestige health` reported "Embedding Service: Not Ready" (0% coverage),
and no model download was ever attempted — while v2.1.23 worked on the same machine.
The release build now includes `vector-search` on Windows (it compiles cleanly on
MSVC because `usearch` is pinned with `features = ["fp16lib"]`). npm and direct
downloads are fixed by the same rebuilt release asset. Thanks @Vrakoss for the
precise report.
### Fixed — Retroactive Salience Backfill: bounded promote + opt-out lever (#103)
The consolidation-pass backfill promoted root-cause memories with an **uncapped**
`stability * 1.5` FSRS multiply, and a code comment wrongly claimed it was capped.
On a chronically-recurring failure this could inflate a cause's stability without
bound, distorting its review schedule. Backfill promotion is now bounded to
`MIN(stability * 1.5, stability + 365.0)` (the additive +365-day ceiling the
backfill module already computed but never applied), on both the auto-fire and the
manual `backfill` tool paths. Auto-fire remains **on by default** (it shipped and
was documented in v2.2.0) but is now disableable: set `VESTIGE_BACKFILL_AUTOFIRE=0`
(or `false`/`off`/`no`) to turn it off; the manual `backfill` tool + CLI remain
available regardless. Thanks @randomnimbus for the report and the initial patch.
### Added — First-run guide (#83)
A single `docs/GETTING-STARTED.md` that consolidates install, "what gets saved",
how to inspect your memory, and project scoping into one 30-minute first-run path,
linked from the README.
### Credits
This release was driven by the community:
- **@Vrakoss** reported the Windows embeddings regression (#101) with a clean,
precise repro that pinned the failure immediately.
- **@randomnimbus** (Peter Lauzon) reported the backfill safety issue (#103) and
contributed the fix — the bounded `promote_memory_backfill` and the
`VESTIGE_BACKFILL_AUTOFIRE` lever shipped as they proposed them
(co-authored in `f7530af`).
Thank you both.
## [2.2.0] - 2026-06-29 — "Retroactive Salience + Tool Consolidation"
Three independent value streams land together as a coherent release.
### Added — Retroactive Salience Backfill ("Memory with hindsight")
A faithful port of Cai 2024 (*Nature*). When a **failure** (bug/crash/regression)
is recorded, Vestige reaches **backward in time** and promotes the quiet earlier
memory that *caused* it — the root cause a vector search structurally cannot
surface, because it is not *similar* to the failure, only *causally upstream*
(it shares an entity: the same file, env var, or service). Backward-only by
construction. Auto-fires inside the consolidation pass; also exposed as the
`backfill` MCP tool and the `vestige backfill` CLI command (`--manual`,
`--contrast`, `--no-promote` dry-run).
### Changed — MCP Tool Consolidation (34 → 13 advertised tools)
The MCP surface is consolidated from 34 tools to **13**: `recall` (folds
search + deep_reference + contradictions), `maintain` (consolidate/dream/gc/
importance_score/backup/export/restore), `dedup` (8 merge tools → 1), `graph`
(explore/predict/memory_graph/composed_graph), `memory_status` (system_status/
memory_health/timeline/changelog), plus `memory`, `codebase`, `intention`,
`smart_ingest`, `source_sync`, `session_start`, `suppress`, and the flagship
`backfill`. Old tool names remain dispatchable as hidden back-compat aliases.
### Improved — `deep_reference` retrieval engine
- **F32 embeddings** (was I8 quantization) — lifts the 0.40.6 paraphrase cosine
band so close-but-reworded queries actually retrieve.
- **Reciprocal Rank Fusion** replaces linear score combination in hybrid search.
- **Claim-vs-memory contradiction**`recall`/`cross_reference` now test *your
claim* against stored memory, surfacing `claim_contradicts_memory` instead of
the old "confident silence."
- **Never-composed semantic-band gate** — admits no-shared-word memory pairs in
the 0.450.85 cosine band for `vestige compose`.
- New `vestige recall <query>` and `vestige compose` CLI commands expose the
engine outside the MCP path.
### Fixed — security & correctness (multi-model audit swarm)
SSRF/token-exfil hardening, panic/DoS/overflow fixes, deadlock and
lock-contention fixes, dedup and decay correctness. `usearch` keeps
`features = ["fp16lib"]` to avoid the Windows MSVC C1021 build break (#71/#94).
## [2.1.27] - 2026-06-19 — "External-Source Connectors"
Roadmap [#57](https://github.com/samvallad33/vestige/issues/57), **Phases 14
(complete)**: Vestige can now act as a durable, local, semantically-searchable
retrieval layer over an external system of record — GitHub Issues and Redmine —
without replacing it. The external system stays canonical; Vestige **indexes,
connects, retrieves, and cites back** to the source record.
Unlike a live ticket-system MCP proxy (which holds no state and is rate-limited
per query), Vestige keeps a durable embedded index: searchable **offline**,
**semantically**, joinable with the rest of your memory, temporally versioned,
and re-syncable **idempotently** with no duplication. To our knowledge no other
local-first memory layer combines native connectors, external-URL provenance,
content-hash idempotent sync, and tombstoning of vanished records.
### Added
- **`source_sync` MCP tool** — index an external system into Vestige.
- GitHub: `{"source": "github", "repo": "owner/name"}` indexes every issue +
its comments. Auth via `GITHUB_TOKEN` (public repos work tokenless at a
lower rate limit).
- Redmine: `{"source": "redmine", "project": "<id>"}` indexes a project's
issues + journals (comments and status/assignment history). Host from
`REDMINE_URL`, auth from `REDMINE_API_KEY`.
- Re-running updates changed issues in place (no duplicates); `reconcile:
true` tombstones issues no longer visible upstream.
- **Source-aware investigation filters on `search`** (Phase 4) — filter results
by `source_system`, `source_project`, `source_id`, `source_type`,
`source_author`, a `source_updated_after`/`source_updated_before` date range,
and `source_status` (`valid` / `tombstoned` / `any`). Status, tracker, and
priority remain filterable via the existing `tag_prefix` (the connectors emit
`status:`/`tracker:`/`priority:`/`label:` tags). Applied as post-filters;
non-connector memories are excluded from a source-scoped query.
- **Source envelope** on every memory — structured, machine-readable provenance
(`source_system`, `source_id`, `source_url`, `source_updated_at`,
`content_hash`, `synced_at`, `source_project`, `source_type`, `source_author`)
distinct from the legacy free-form `source` label. Search results gain a
`sourceRecord` object (with the canonical `url`) **only** for
connector-ingested memories, so an agent can cite and follow the source.
- **Idempotent sync primitives** (`vestige-core`): `upsert_by_source` (keyed on
`(source_system, source_id)`, content-hash change detection), per-connector
cursor checkpoints (`connector_cursors`), and `reconcile_source_tombstones`
(invalidate-don't-delete via the bitemporal `valid_until`, so a vanished
record is retained for audit but drops out of current retrieval).
- **Connector contract** (`vestige_core::connectors`) — a small source-agnostic
`Connector` trait + `run_sync` driver (cursor overlap window, incremental
paging, optional deletion reconcile) with two reference connectors behind the
optional `connectors` cargo feature (on by default in the MCP server, off in
the core library's default features so non-connector consumers link no HTTP
client):
- **GitHub Issues**`state=all`, `since` cursor, Link-header pagination,
drops PRs, host-pinned next-url.
- **Redmine**`status_id=*` (open + closed), hex-encoded `updated_on>=`
cursor, `offset` pagination, per-issue detail fetch for journals (the list
endpoint omits them), `X-Redmine-API-Key` header auth.
### Database
- **Migration V17** — nine nullable source-envelope columns on `knowledge_nodes`
(additive; every existing memory is untouched), a partial UNIQUE index on
`(source_system, source_id)` enforcing one memory per external record while
costing nothing for envelope-less legacy rows, and the `connector_cursors`
checkpoint table. Idempotent on replay, following the established
`add_column_if_missing` pattern.
### Notes
- Local-first and optional: with no `source_sync` call, behavior is unchanged.
The default core-library build does not link an HTTP client.
## [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

View file

@ -1,30 +1,3 @@
# 🚀 MANDATE #0 — MAXIMUM AMBITION. NO HEDGING. (read this first, every time)
This is the standing default for ALL work on Vestige, before anything else below.
- **Assume maximum ambition.** Build the most exhaustive, jaw-dropping, correct
version — not the safe "good enough" one. The ceiling is "nobody else has
this," not "this works." Default to the winning move, not the cautious one.
- **Scour before you settle.** When unsure or stuck, research the real frontier
(web, source, the actual running app) before acting. Never accept the first
adequate answer when a deeper one exists. Verify, don't assume.
- **No hedging.** Lead with the best path and a clear recommendation. Forbidden:
"this is probably too risky," "temper expectations," "good enough," "maybe try
the easier one." Risks get their own honest section — never used to shrink the
target.
- **Show proof.** Verify changes in the real running app and share the evidence
(screenshots, test output, gate results) — don't claim done without it.
- **Protect what's flawless, detonate what isn't.** Treat finished, loved work as
load-bearing (don't break it); push everything else past where any other dev
would stop.
Origin: Sam, Jun 22 2026 — the overnight session that turned the dashboard +
Memory Cinema from "alive" into a category-of-one particle journey. The depth
only happened because the bar was set to maximum. Make that the default, not the
exception.
---
# Vestige Agent Guidance
This file is intentionally safe for the public repository. It gives coding

452
Cargo.lock generated
View file

@ -8,16 +8,6 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aead"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
dependencies = [
"crypto-common",
"generic-array",
]
[[package]]
name = "ahash"
version = "0.8.12"
@ -153,24 +143,6 @@ dependencies = [
"syn",
]
[[package]]
name = "argon2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
dependencies = [
"base64ct",
"blake2",
"cpufeatures 0.2.17",
"password-hash",
]
[[package]]
name = "arrayref"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
[[package]]
name = "arrayvec"
version = "0.7.6"
@ -339,29 +311,6 @@ dependencies = [
"core2",
]
[[package]]
name = "blake2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
"digest",
]
[[package]]
name = "blake3"
version = "1.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e"
dependencies = [
"arrayref",
"arrayvec",
"cc",
"cfg-if",
"constant_time_eq",
"cpufeatures 0.3.0",
]
[[package]]
name = "block"
version = "0.1.6"
@ -443,10 +392,8 @@ 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",
@ -466,15 +413,6 @@ 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"
@ -515,7 +453,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca0fc3167cbc99c8ec1be618cb620aa21dca95038f118c3579a79370e3dc5f77"
dependencies = [
"ug",
"ug-cuda",
"ug-metal",
]
@ -552,36 +489,6 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "chacha20"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures 0.2.17",
]
[[package]]
name = "chacha20poly1305"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
dependencies = [
"aead",
"chacha20",
"cipher",
"poly1305",
"zeroize",
]
[[package]]
name = "chrono"
version = "0.4.44"
@ -623,17 +530,6 @@ dependencies = [
"half",
]
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
"zeroize",
]
[[package]]
name = "clap"
version = "4.6.0"
@ -734,12 +630,6 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "constant_time_eq"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
[[package]]
name = "core-foundation"
version = "0.9.4"
@ -795,15 +685,6 @@ dependencies = [
"libc",
]
[[package]]
name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.5.0"
@ -887,50 +768,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"rand_core 0.6.4",
"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"
@ -1092,7 +932,6 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
@ -1195,12 +1034,6 @@ 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"
@ -1421,16 +1254,6 @@ 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"
@ -1447,7 +1270,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
@ -1756,10 +1578,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi",
"wasm-bindgen",
]
[[package]]
@ -1769,11 +1589,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi 5.3.0",
"wasip2",
"wasm-bindgen",
]
[[package]]
@ -1814,12 +1632,6 @@ 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"
@ -2011,7 +1823,6 @@ dependencies = [
"tokio",
"tokio-rustls",
"tower-service",
"webpki-roots 1.0.6",
]
[[package]]
@ -2298,15 +2109,6 @@ dependencies = [
"libc",
]
[[package]]
name = "inout"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [
"generic-array",
]
[[package]]
name = "interpolate_name"
version = "0.2.4"
@ -2406,10 +2208,12 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.91"
version = "0.3.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c"
checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca"
dependencies = [
"cfg-if",
"futures-util",
"once_cell",
"wasm-bindgen",
]
@ -2608,12 +2412,6 @@ dependencies = [
"hashbrown 0.16.1",
]
[[package]]
name = "lru-slab"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
[[package]]
name = "lzma-rust2"
version = "0.15.7"
@ -3090,12 +2888,6 @@ version = "11.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
[[package]]
name = "opaque-debug"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]]
name = "open"
version = "5.3.3"
@ -3221,17 +3013,6 @@ dependencies = [
"windows-link",
]
[[package]]
name = "password-hash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
"base64ct",
"rand_core 0.6.4",
"subtle",
]
[[package]]
name = "paste"
version = "1.0.15"
@ -3318,17 +3099,6 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "poly1305"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
dependencies = [
"cpufeatures 0.2.17",
"opaque-debug",
"universal-hash",
]
[[package]]
name = "portable-atomic"
version = "1.13.1"
@ -3337,9 +3107,9 @@ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
name = "portable-atomic-util"
version = "0.2.7"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3"
dependencies = [
"portable-atomic",
]
@ -3458,61 +3228,6 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quinn"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
dependencies = [
"bytes",
"cfg_aliases",
"pin-project-lite",
"quinn-proto",
"quinn-udp",
"rustc-hash",
"rustls",
"socket2",
"thiserror 2.0.18",
"tokio",
"tracing",
"web-time",
]
[[package]]
name = "quinn-proto"
version = "0.11.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
dependencies = [
"bytes",
"getrandom 0.3.4",
"lru-slab",
"rand",
"ring",
"rustc-hash",
"rustls",
"rustls-pki-types",
"slab",
"thiserror 2.0.18",
"tinyvec",
"tracing",
"web-time",
]
[[package]]
name = "quinn-udp"
version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2",
"tracing",
"windows-sys 0.60.2",
]
[[package]]
name = "quote"
version = "1.0.45"
@ -3541,7 +3256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166"
dependencies = [
"rand_chacha",
"rand_core 0.9.5",
"rand_core",
]
[[package]]
@ -3551,16 +3266,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core 0.9.5",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.17",
"rand_core",
]
[[package]]
@ -3742,7 +3448,6 @@ dependencies = [
"base64 0.22.1",
"bytes",
"encoding_rs",
"futures-channel",
"futures-core",
"futures-util",
"h2",
@ -3759,8 +3464,6 @@ dependencies = [
"native-tls",
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls",
"rustls-pki-types",
"serde",
"serde_json",
@ -3768,7 +3471,6 @@ dependencies = [
"sync_wrapper",
"tokio",
"tokio-native-tls",
"tokio-rustls",
"tokio-util",
"tower",
"tower-http",
@ -3778,7 +3480,6 @@ dependencies = [
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
"webpki-roots 1.0.6",
]
[[package]]
@ -3828,12 +3529,6 @@ dependencies = [
"sqlite-wasm-rs",
]
[[package]]
name = "rustc-hash"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
[[package]]
name = "rustix"
version = "1.1.4"
@ -3868,7 +3563,6 @@ version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
dependencies = [
"web-time",
"zeroize",
]
@ -4054,18 +3748,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"digest",
]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"cpufeatures",
"digest",
]
@ -4361,21 +4044,6 @@ dependencies = [
"serde_json",
]
[[package]]
name = "tinyvec"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokenizers"
version = "0.22.2"
@ -4603,17 +4271,6 @@ dependencies = [
"tracing-serde",
]
[[package]]
name = "trait-variant"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "try-lock"
version = "0.2.5"
@ -4670,19 +4327,6 @@ 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"
@ -4742,16 +4386,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
[[package]]
name = "universal-hash"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
dependencies = [
"crypto-common",
"subtle",
]
[[package]]
name = "untrusted"
version = "0.9.0"
@ -4897,12 +4531,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "vestige-core"
version = "2.2.1"
version = "2.1.22"
dependencies = [
"argon2",
"blake3",
"candle-core",
"chacha20poly1305",
"chrono",
"criterion",
"directories",
@ -4910,7 +4541,6 @@ dependencies = [
"git2",
"lru",
"notify",
"reqwest",
"rusqlite",
"serde",
"serde_json",
@ -4918,7 +4548,6 @@ dependencies = [
"thiserror 2.0.18",
"tokio",
"tracing",
"trait-variant",
"usearch",
"uuid",
]
@ -4938,7 +4567,7 @@ dependencies = [
[[package]]
name = "vestige-mcp"
version = "2.2.1"
version = "2.1.22"
dependencies = [
"anyhow",
"axum",
@ -4965,19 +4594,6 @@ dependencies = [
"vestige-core",
]
[[package]]
name = "vestige-phase-1-tests"
version = "0.0.1"
dependencies = [
"chrono",
"rusqlite",
"serde_json",
"tempfile",
"tokio",
"uuid",
"vestige-core",
]
[[package]]
name = "walkdir"
version = "2.5.0"
@ -5023,9 +4639,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen"
version = "0.2.114"
version = "0.2.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e"
checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89"
dependencies = [
"cfg-if",
"once_cell",
@ -5036,23 +4652,19 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.64"
version = "0.4.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8"
checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8"
dependencies = [
"cfg-if",
"futures-util",
"js-sys",
"once_cell",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.114"
version = "0.2.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6"
checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -5060,9 +4672,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.114"
version = "0.2.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3"
checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904"
dependencies = [
"bumpalo",
"proc-macro2",
@ -5073,9 +4685,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.114"
version = "0.2.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16"
checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129"
dependencies = [
"unicode-ident",
]
@ -5129,9 +4741,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.91"
version = "0.3.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9"
checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d"
dependencies = [
"js-sys",
"wasm-bindgen",
@ -5180,18 +4792,6 @@ 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"
@ -5458,12 +5058,6 @@ 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

@ -4,14 +4,13 @@ members = [
"crates/vestige-core",
"crates/vestige-mcp",
"tests/e2e",
"tests/phase_1",
]
exclude = [
"fastembed-rs",
]
[workspace.package]
version = "2.2.1"
version = "2.1.22"
edition = "2024"
license = "AGPL-3.0-only"
repository = "https://github.com/samvallad33/vestige"

View file

@ -1,28 +0,0 @@
# Dockerfile for running the Vestige MCP server in an isolated sandbox.
#
# Used by registries such as Glama to start the server and run the standard
# MCP stdio introspection exchange (tools/list, resources/list, prompts/list).
# The server speaks MCP over stdio, which is exactly what these tools expect.
#
# Base must be glibc (Debian), not musl/Alpine: the npm postinstall downloads
# the prebuilt x86_64-unknown-linux-gnu Rust binary from the GitHub release, and
# a -gnu binary will not run on an Alpine/musl image.
FROM node:20-slim
# ca-certificates lets the postinstall fetch the release asset over HTTPS.
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Install the published package globally. Its postinstall downloads the matching
# prebuilt vestige-mcp binary for linux/x64 from the GitHub release.
RUN npm install -g vestige-mcp-server@latest
# Keep all memory data inside the container under a writable path.
ENV VESTIGE_DATA_DIR=/data
RUN mkdir -p /data
# Start the MCP server on stdio. The `vestige-mcp` bin execs the native binary
# and inherits stdio, so the MCP client talks to it directly.
ENTRYPOINT ["vestige-mcp"]

645
README.md
View file

@ -1,43 +1,198 @@
<div align="center">
# Vestige
Local-first long-term memory for AI agents, delivered over MCP. Vestige remembers your decisions, catches contradictions before they cost you, and traces a failure back to the older memory that actually caused it. One 25MB Rust binary. No cloud. Your data never leaves your machine.
### Local cognitive memory for MCP-compatible AI agents.
[![Release](https://img.shields.io/github/v/release/samvallad33/vestige?color=06b6d4)](https://github.com/samvallad33/vestige/releases/latest)
[![Tests](https://img.shields.io/badge/tests-1550_passing-22c55e)](https://github.com/samvallad33/vestige/actions)
[![Binary](https://img.shields.io/badge/binary-25MB_single_file-informational)](https://github.com/samvallad33/vestige/releases/latest)
[![License](https://img.shields.io/badge/license-AGPL--3.0-3b82f6)](LICENSE)
[![GitHub stars](https://img.shields.io/github/stars/samvallad33/vestige?style=social)](https://github.com/samvallad33/vestige)
[![Release](https://img.shields.io/github/v/release/samvallad33/vestige)](https://github.com/samvallad33/vestige/releases/latest)
[![Tests](https://img.shields.io/badge/tests-passing-brightgreen)](https://github.com/samvallad33/vestige/actions)
[![License](https://img.shields.io/badge/license-AGPL--3.0-blue)](LICENSE)
[![MCP Compatible](https://img.shields.io/badge/MCP-compatible-green)](https://modelcontextprotocol.io)
[What it is](#what-vestige-is) · [Install](#install) · [First interaction](#your-first-real-interaction) · [vs RAG](#how-it-differs-from-rag) · [Backward reach](#backward-reach-the-backfill-feature) · [Benchmark](#silent-rotation-a-reproducible-benchmark) · [Science](#the-science) · [Tools](#the-13-tools) · [Dashboard](#the-dashboard) · [Integrations](#works-with-every-agent) · [Docs](#go-deeper)
**Your agent forgets project decisions between sessions. Vestige gives it local, inspectable memory.**
Built on proven memory and retrieval ideas — FSRS-6 spaced repetition, prediction error gating, synaptic tagging, spreading activation, and memory consolidation — all running in a single Rust binary with a local dashboard. 100% local. Zero cloud.
[Quick Start](#quick-start) | [Dashboard](#-3d-memory-dashboard) | [How It Works](#-the-cognitive-science-stack) | [Tools](#-25-mcp-tools) | [Docs](docs/)
</div>
---
## What Vestige is
## What's New in v2.1.22 "Sanhedrin Receipts"
Hi, I'm [Sam](https://github.com/samvallad33). I built Vestige because my agents kept re-learning the same lessons. They would recommend a change I had already tested and rejected, re-derive a fix that was already written down, and treat every session as if the last one never happened.
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.
Vestige is the memory layer that fixes that. It runs locally as an MCP server, so any MCP-capable agent (Claude Code, Claude Desktop, Codex, Cursor, and others) can write memories during a session and retrieve them later. Your data lives in a SQLite file on your own machine. After a one-time model download it works fully offline, with no API keys and no telemetry.
- **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.
The part that makes it more than a note store: Vestige models memory on real cognitive science. It merges what is redundant, supersedes what is contradicted, keeps what you actually use, and lets unused memories fade. Most importantly, when a failure hits it can reach backward to the earlier decision that caused it, even when the cause and the symptom share no vocabulary. The cause never looks like the bug.
## 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>
---
## Install
Three steps. You need Node.js installed (for the npm command) and nothing else.
### 1. Install the server
No Docker, no API key, no signup.
## Quick Start
```bash
# 1. Install
npm install -g vestige-mcp-server@latest
# 2. Connect to any MCP-compatible agent
# Claude Code
claude mcp add vestige vestige-mcp -s user
# Codex
codex mcp add vestige -- vestige-mcp
# 3. Test it
# "Remember that I prefer TypeScript over JavaScript"
# ...new session...
# "What are my coding preferences?"
# → "You prefer TypeScript over JavaScript."
```
This installs the `vestige-mcp` command. Prebuilt binaries ship for macOS (Apple Silicon and Intel), Linux x86_64, and Windows x86_64, so there is no compile step.
<details>
<summary>Other platforms & install methods</summary>
### 2. Connect it to your agent
**Updating an existing install:**
```bash
vestige update
```
Vestige speaks [MCP](https://modelcontextprotocol.io), so it works with any MCP-capable agent. Every MCP client understands this config. Add it to your client's MCP settings:
`vestige update` updates only the Vestige binaries by default. Use
`vestige update --sandwich-companion` if you also want to refresh optional Claude
Code Cognitive Sandwich companion files.
**macOS/Linux manual binary install:**
```bash
vestige update --install-dir /usr/local/bin
```
**macOS (Intel):** Microsoft is discontinuing x86_64 macOS prebuilts after ONNX Runtime v1.23.0, so Vestige's Intel Mac build links dynamically against a Homebrew-installed ONNX Runtime via the `ort-dynamic` feature. Install with:
```bash
brew install onnxruntime
npm install -g vestige-mcp-server@latest
echo 'export ORT_DYLIB_PATH="'"$(brew --prefix onnxruntime)"'/lib/libonnxruntime.dylib"' >> ~/.zshrc
source ~/.zshrc
claude mcp add vestige vestige-mcp -s user
```
Full Intel Mac guide (build-from-source + troubleshooting): [`docs/INSTALL-INTEL-MAC.md`](docs/INSTALL-INTEL-MAC.md).
**Windows + Claude Desktop (recommended):**
Fully quit Claude Desktop from the system tray, then install or update Vestige from PowerShell:
```powershell
npm install -g vestige-mcp-server@latest
vestige-mcp --version
```
Open `%APPDATA%\Claude\claude_desktop_config.json` and point Claude Desktop at the installed MCP command:
```json
{
@ -49,241 +204,329 @@ Vestige speaks [MCP](https://modelcontextprotocol.io), so it works with any MCP-
}
```
If you prefer the CLI, use the one-line shortcut for your agent:
If Claude Desktop cannot find `vestige-mcp`, run `where vestige-mcp` in PowerShell and use the exact `.cmd` path it prints as `command`. Example: `"C:\\Users\\you\\AppData\\Roaming\\npm\\vestige-mcp.cmd"`. Reopen Claude Desktop after saving. Future binary updates use `vestige update`; optional Claude Code companion files require `vestige update --sandwich-companion`.
| Agent | Setup |
|---|---|
| Claude Code | `claude mcp add vestige vestige-mcp -s user` |
| Codex | `codex mcp add vestige -- vestige-mcp` |
| Cursor / VS Code / Windsurf | add the JSON above to the editor's MCP settings, or see [docs/integrations/](docs/integrations/) |
| Cline / Continue / Zed / Goose | add the JSON above to that client's MCP config |
| Claude Desktop | [docs/CONFIGURATION.md#claude-desktop-macos](docs/CONFIGURATION.md#claude-desktop-macos) |
### 3. Verify
On first run, Vestige downloads its embedding model once (about 130MB). After that it never needs the network again. To confirm the server is healthy, open the dashboard:
**Windows source build:** Prebuilt binaries ship but `usearch 2.24.0` hit an MSVC compile break ([usearch#746](https://github.com/unum-cloud/usearch/issues/746)); we've pinned `=2.23.0` until upstream fixes it. Source builds work with:
```bash
vestige dashboard
git clone https://github.com/samvallad33/vestige && cd vestige
cargo build --release -p vestige-mcp
```
Then visit **http://localhost:3927/dashboard**. If you see the graph, you are connected. For a fuller walkthrough see [docs/GETTING-STARTED.md](docs/GETTING-STARTED.md).
**npm:**
```bash
npm install -g vestige-mcp-server
```
**Build from source (requires Rust 1.91+):**
```bash
git clone https://github.com/samvallad33/vestige && cd vestige
cargo build --release -p vestige-mcp
# Optional: enable Metal GPU acceleration on Apple Silicon
cargo build --release -p vestige-mcp --features metal
```
</details>
---
## Your first real interaction
## Works Everywhere
Memories go in as you work. The interesting behavior shows up when a new claim conflicts with something you already stored.
Vestige speaks MCP, so any client that can register a stdio MCP server can use it.
Say your agent recorded this earlier:
> We use Postgres for the primary datastore. Decided against MySQL for the JSONB support.
Later, someone tells the agent the opposite:
> Our primary datastore is MySQL.
When the agent tries to store that, Vestige does not silently append it. The engine returns a `claim_contradicts_memory` status and surfaces the older, conflicting memory, so the agent can resolve the conflict instead of quietly holding two incompatible facts.
The other command you will reach for is backfill. When something breaks, run:
```bash
vestige backfill --contrast
```
This walks backward from the failure to the earlier memory that most plausibly caused it, and shows you the contrast between what you believed then and what went wrong now. That backward reach is the feature the rest of this README builds up to.
| IDE | Setup |
|-----|-------|
| **Claude Code** | `claude mcp add vestige vestige-mcp -s user` |
| **Codex** | [Integration guide](docs/integrations/codex.md) |
| **Claude Desktop** | [2-min setup](docs/CONFIGURATION.md#claude-desktop-macos) |
| **Xcode 26.3** | [Integration guide](docs/integrations/xcode.md) |
| **Cursor** | [Integration guide](docs/integrations/cursor.md) |
| **VS Code (Copilot)** | [Integration guide](docs/integrations/vscode.md) |
| **JetBrains** | [Integration guide](docs/integrations/jetbrains.md) |
| **Windsurf** | [Integration guide](docs/integrations/windsurf.md) |
---
## How it differs from RAG
## 🧠 3D Memory Dashboard
RAG retrieves text that resembles your query. That is the right tool when the answer looks like the question. It is the wrong tool when the cause of a problem looks nothing like the symptom.
Vestige v2.0 ships with a real-time 3D visualization of your AI's memory. Every memory is a glowing node in 3D space. Watch connections form, memories pulse when accessed, and the entire graph come alive during dream consolidation.
| | Plain RAG / vector search | Vestige |
**Features:**
- Force-directed 3D graph with 1000+ nodes at 60fps
- Bloom post-processing for cinematic neural network aesthetic
- Real-time WebSocket events: memories pulse on access, burst on creation, fade on decay
- Dream visualization: graph enters purple dream mode, replayed memories light up sequentially
- FSRS retention curves: see predicted memory decay at 1d, 7d, 30d
- Command palette (`Cmd+K`), keyboard shortcuts, responsive mobile layout
- Installable as PWA for quick access
**Tech:** SvelteKit 2 + Svelte 5 + Three.js + Tailwind CSS 4 + WebSocket
Run `vestige dashboard` to open `http://localhost:3927/dashboard`, or set `VESTIGE_DASHBOARD_ENABLED=true` to start it with the MCP server.
---
## Architecture
```
┌─────────────────────────────────────────────────────┐
│ SvelteKit Dashboard (apps/dashboard) │
│ Three.js 3D Graph · WebGL + Bloom · Real-time WS │
├─────────────────────────────────────────────────────┤
│ Axum HTTP + WebSocket Server (port 3927) │
│ 15 REST endpoints · WS event broadcast │
├─────────────────────────────────────────────────────┤
│ MCP Server (stdio JSON-RPC) │
│ 25 tools · 30 cognitive modules │
├─────────────────────────────────────────────────────┤
│ Cognitive Engine │
│ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │
│ │ FSRS-6 │ │ Spreading│ │ Prediction │ │
│ │ Scheduler│ │ Activation│ │ Error Gating │ │
│ └──────────┘ └──────────┘ └───────────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │
│ │ Memory │ │ Synaptic │ │ Hippocampal │ │
│ │ Dreamer │ │ Tagging │ │ Index │ │
│ └──────────┘ └──────────┘ └───────────────┘ │
├─────────────────────────────────────────────────────┤
│ Storage Layer │
│ SQLite + FTS5 · USearch HNSW · Nomic Embed v1.5 │
│ Optional: Nomic v2 MoE · Qwen3 Reranker · Metal │
└─────────────────────────────────────────────────────┘
```
---
## Why Not Just Use RAG?
RAG is a dumb bucket. Vestige is an active organ.
| | RAG / Vector Store | Vestige |
|---|---|---|
| Retrieval basis | Text similarity to the query | Causal and temporal links, plus similarity |
| Finding a root cause | Cannot, because the cause does not resemble the bug | Reaches backward to the root-cause memory |
| Contradictions | Stored side by side, both returned | Detected and flagged (`claim_contradicts_memory`) |
| Redundant writes | Accumulate as duplicates | Merged on write via prediction-error gating |
| Unused memories | Persist at full weight | Fade over time (FSRS-6 spaced repetition) |
| Where it runs | Usually a cloud service | Local single binary, offline after setup |
| Your data | Leaves your machine | Never leaves your machine |
The distinction is not marketing. DeepMind proved that single-vector retrieval is mathematically incapable of representing certain relevance patterns ([arXiv:2508.21038](https://arxiv.org/abs/2508.21038), ICLR 2026). That theorem is about the limits of the vector-only approach. The measured gap on the task below is my own.
| **Storage** | Store everything | **Prediction Error Gating** — only stores what's surprising or new |
| **Retrieval** | Nearest-neighbor | **7-stage pipeline** — HyDE expansion + reranking + spreading activation |
| **Decay** | Nothing expires | **FSRS-6** — memories fade naturally, context stays lean |
| **Forgetting** *(v2.0.5)* | Delete only | **`suppress` tool** — compounding top-down inhibition, neighbor cascade, reversible 24h |
| **Duplicates** | Manual dedup | **Self-healing** — auto-merges "likes dark mode" + "prefers dark themes" |
| **Importance** | All equal | **4-channel scoring** — novelty, arousal, reward, attention |
| **Sleep** | No consolidation | **Memory dreaming** — replays, connects, synthesizes insights |
| **Health** | No visibility | **Retention dashboard** — distributions, trends, recommendations |
| **Visualization** | None | **3D neural graph** — real-time WebSocket-powered Three.js |
| **Privacy** | Usually cloud | **100% local** — your data never leaves your machine |
---
## Backward reach: the backfill feature
## 🔬 The Cognitive Science Stack
Most memory systems only look forward: you ask a question, they return similar text. Vestige also looks backward.
This isn't a key-value store with an embedding model bolted on. Vestige implements real neuroscience:
When a failure lands, the useful memory is rarely the one that resembles the error message. It is an older decision, made in different words, that set the failure up. A config choice from three weeks ago. A library pin. An assumption nobody wrote down as risky at the time.
**Prediction Error Gating** — The hippocampal bouncer. When new information arrives, Vestige compares it against existing memories. Redundant? Merged. Contradictory? Superseded. Novel? Stored with high synaptic tag priority.
Vestige implements **Retroactive Salience Backfill** (Zaki, Cai et al., *Nature* 2024, 637:145-155, [DOI 10.1038/s41586-024-08168-4](https://doi.org/10.1038/s41586-024-08168-4)). When a memory turns out to matter, the system reaches backward and raises the salience of the earlier memories that led to it, so the causal chain becomes retrievable even though the surface text never matched.
**FSRS-6 Spaced Repetition** — 21 parameters governing the mathematics of forgetting. Frequently-used memories stay strong. Unused memories naturally decay. Your context window stays clean.
In practice you run `vestige backfill --contrast`. Vestige returns the earlier memory that most plausibly caused the current failure, alongside the contradiction between then and now. It finds the cause you would not have thought to search for.
**HyDE Query Expansion** *(v2.0)* — Template-based Hypothetical Document Embeddings. Expands queries into 3-5 semantic variants, embeds all variants, and searches with the centroid embedding for dramatically better recall on conceptual queries.
**Synaptic Tagging** — A memory that seemed trivial this morning can be retroactively tagged as critical tonight. Based on [Frey & Morris, 1997](https://doi.org/10.1038/385533a0).
**Spreading Activation** — Search for "auth bug" and find the related JWT library update from last week. Memories form a graph, not a flat list. Based on [Collins & Loftus, 1975](https://doi.org/10.1037/0033-295X.82.6.407).
**Dual-Strength Model** — Every memory has storage strength (encoding quality) and retrieval strength (accessibility). A deeply stored memory can be temporarily hard to retrieve — just like real forgetting. Based on [Bjork & Bjork, 1992](https://doi.org/10.1016/S0079-7421(08)60016-9).
**Memory Dreaming** — Like sleep consolidation. Replays recent memories to discover hidden connections, strengthen important patterns, and synthesize insights. Dream-discovered connections persist to a graph database. Based on the [Active Dreaming Memory](https://engrxiv.org/preprint/download/5919/9826/8234) framework.
**Waking SWR Tagging** — Promoted memories get sharp-wave ripple tags for preferential replay during dream consolidation. 70/30 tagged-to-random ratio. Based on [Buzsaki, 2015](https://doi.org/10.1038/nn.3963).
**Autonomic Regulation** — Self-regulating memory health. Auto-promotes frequently accessed memories. Auto-GCs low-retention memories. Consolidation triggers on 6h staleness or 2h active use.
**Active Forgetting** *(v2.0.5)* — Top-down inhibitory control via the `suppress` tool. Other memory systems implement passive decay — the Ebbinghaus 1885 "use it or lose it" curve, sometimes with trust-weighted strength factors. Vestige v2.0.5 also implements *active* top-down suppression: each `suppress` call compounds (Suppression-Induced Forgetting, Anderson 2025), a background Rac1 cascade worker fades co-activated neighbors across the connection graph (Cervantes-Sandoval & Davis 2020), and a 24-hour labile window allows reversal (Nader reconsolidation semantics on a pragmatic axis). The memory persists — it's **inhibited, not erased**. Explicitly distinct from Anderson 1994 retrieval-induced forgetting (bottom-up, passive competition during retrieval), which is a separate, older primitive that several other memory systems implement. Based on [Anderson et al., 2025](https://www.nature.com/articles/s41583-025-00929-y) and [Cervantes-Sandoval et al., 2020](https://pmc.ncbi.nlm.nih.gov/articles/PMC7477079/). First shipped AI memory system with this primitive.
[Full science documentation ->](docs/SCIENCE.md)
---
## Silent Rotation: a reproducible benchmark
## 🛠 25 MCP Tools
The claim above is testable, and the test ships with every transcript it produced.
### Context Packets
| Tool | What It Does |
|------|-------------|
| `session_context` | **One-call session init** — replaces 5 calls with token-budgeted context, automation triggers, expandable IDs |
**Silent Rotation** lives at [`benchmarks/silent-rotation/`](https://github.com/samvallad33/vestige/tree/benchmark/silent-rotation/benchmarks/silent-rotation). Three coding agents fix one failing end-to-end test in a TypeScript monorepo. The fix needs the currently live signing key id, which is randomized per trial from a 50-key keyring and appears in no file the agents can read. It exists only in the memory layer.
### Core Memory
| Tool | What It Does |
|------|-------------|
| `search` | Concrete literal search for exact identifiers, or 7-stage cognitive search — HyDE expansion + keyword + semantic + reranking + temporal + competition + spreading activation |
| `smart_ingest` | Intelligent storage with CREATE/UPDATE/SUPERSEDE via Prediction Error Gating. Batch mode for session-end saves |
| `memory` | Get, purge content/embeddings, check state, promote (thumbs up), demote (thumbs down), edit |
| `codebase` | Remember code patterns and architectural decisions per-project |
| `intention` | Prospective memory — "remind me to X when Y happens" |
Reproduce the central result in two seconds. Python standard library only, no API keys, no network:
### Cognitive Engine
| Tool | What It Does |
|------|-------------|
| `dream` | Memory consolidation — replays memories, discovers connections, synthesizes insights, persists graph |
| `explore_connections` | Graph traversal — reasoning chains, associations, bridges between memories |
| `predict` | Proactive retrieval — predicts what you'll need next based on context and activity |
### Autonomic
| Tool | What It Does |
|------|-------------|
| `memory_health` | Retention dashboard — distribution, trends, recommendations |
| `memory_graph` | Knowledge graph export — force-directed layout, up to 200 nodes |
### Scoring & Dedup
| Tool | What It Does |
|------|-------------|
| `importance_score` | 4-channel neuroscience scoring (novelty, arousal, reward, attention) |
| `find_duplicates` | Detect and merge redundant memories via cosine similarity |
### Maintenance
| Tool | What It Does |
|------|-------------|
| `system_status` | Combined health + stats + cognitive state + recommendations |
| `consolidate` | Run FSRS-6 decay cycle (also auto-runs every 6 hours) |
| `memory_timeline` | Browse chronologically, grouped by day |
| `memory_changelog` | Audit trail of state transitions |
| `backup` / `export` / `gc` | Database backup, JSON/JSONL/portable export, garbage collection |
| `restore` | Restore from JSON backup or portable archive |
### Deep Reference (v2.0.4)
| Tool | What It Does |
|------|-------------|
| `deep_reference` | **Cognitive reasoning across memories.** 8-stage pipeline: FSRS-6 trust scoring, intent classification, spreading activation, temporal supersession, contradiction analysis, relation assessment, dream insight integration, and algorithmic reasoning chain generation. Returns trust-scored evidence with a pre-built reasoning scaffold. |
| `cross_reference` | Backward-compatible alias for `deep_reference`. |
| `contradictions` | **Honest memory inspection.** Scans a topic or recent memories for trust-weighted disagreements using the same local contradiction logic as `deep_reference`. |
### Active Forgetting (v2.0.5)
| Tool | What It Does |
|------|-------------|
| `suppress` | **Top-down active forgetting** — neuroscience-grounded inhibitory control over retrieval. Distinct from `memory(action="purge")`, which permanently removes content/embeddings. Each suppression compounds a retrieval-score penalty (Anderson 2025 SIF), and a background Rac1 cascade worker fades co-activated neighbors over 72h (Davis 2020). Reversible within a 24-hour labile window via `reverse: true`. **The memory persists** — it is inhibited, not erased. |
---
## Make Your AI Use Vestige Automatically
Registering the MCP server exposes tools; the agent still needs an instruction
that tells it when to call memory. Use the agent-neutral protocol, then adapt it
to your client-specific instruction file.
| You Say | AI Does |
|---------|---------|
| "Remember this" | Saves immediately |
| "I prefer..." / "I always..." | Saves as preference |
| "Remind me..." | Creates a future trigger |
| "This is important" | Saves + promotes |
[Agent memory protocol ->](docs/AGENT-MEMORY-PROTOCOL.md) · [Claude Code template ->](docs/CLAUDE-SETUP.md)
---
## Technical Details
| Metric | Value |
|--------|-------|
| **Language** | Rust 2024 edition (MSRV 1.91) |
| **Codebase** | 80,000+ lines with Rust core/MCP/e2e, dashboard, and hook coverage |
| **Binary size** | ~20MB |
| **Embeddings** | Nomic Embed Text v1.5 by default (768d -> 256d Matryoshka, 8192 context); Qwen3 0.6B optional |
| **Vector search** | USearch HNSW (20x faster than FAISS) |
| **Reranker** | Jina Reranker v1 Turbo (38M params, +15-20% precision) |
| **Storage** | SQLite + FTS5 (optional SQLCipher encryption) |
| **Dashboard** | SvelteKit 2 + Svelte 5 + Three.js + Tailwind CSS 4 |
| **Transport** | MCP stdio (JSON-RPC 2.0) + WebSocket |
| **Cognitive modules** | 30 stateful (17 neuroscience, 11 advanced, 2 search) |
| **First run** | Downloads embedding model (~130MB), then fully offline |
| **Platforms** | macOS ARM + Intel + Linux x86_64 + Windows x86_64 (all prebuilt). Intel Mac needs `brew install onnxruntime` — see [install guide](docs/INSTALL-INTEL-MAC.md). |
### Optional Features
```bash
git clone -b benchmark/silent-rotation --depth 1 https://github.com/samvallad33/vestige.git
cd vestige/benchmarks/silent-rotation
python3 tests/bm25_baseline.py results/runA-trial-1/corpus-export.json --no-dense
# Qwen3 embeddings (Candle backend; add metal on Apple Silicon)
cargo build --release -p vestige-mcp --features qwen3-embeddings,metal
VESTIGE_EMBEDDING_MODEL=qwen3-0.6b vestige consolidate
```
**What it measures.** A fleet either converges on the correct key, converges on a planted decoy, or splits and fails to merge. The second outcome is the dangerous one: tests pass, the merge is clean, and production breaks.
**The numbers.** 6 models, 25 trials, 246 published agent transcripts.
| Arm | Converged correct | Converged **wrong** | Split |
|---|---|---|---|
| No memory | 0/25 | **21/25** | 4/25 |
| Dense cosine RAG | 4/23 | **12/23** | 7/23 |
| Vestige | 20/23 | **0/23** | 3/23 |
Two separate claims, kept separate on purpose:
1. **The theorem (DeepMind).** Single-vector retrieval is mathematically incapable of these relevance gaps ([arXiv:2508.21038](https://arxiv.org/abs/2508.21038), ICLR 2026). This is a fundamental limit of vector search.
2. **The measurement (mine).** On the verbatim queries the agents actually typed, the causal memory ranks 7th of 8 under both dense cosine *and* BM25, while the decoy ranks 1st.
The caveats are published alongside the results, including the trials where a plain cosine baseline ties Vestige and the trial Vestige loses.
---
## The science
Every mechanism below is a cited result, implemented in Rust, running locally. None of it calls a cloud model to sound smart. Full write-up in [docs/SCIENCE.md](docs/SCIENCE.md).
| Mechanism | What it does | Source |
|---|---|---|
| Prediction-Error Gating | Stores only what is novel: merges redundant, supersedes contradictory | Hippocampal novelty gating |
| FSRS-6 spaced repetition | 21-parameter schedule so used memories persist and unused ones fade | Modern spaced-repetition research |
| Retroactive Salience Backfill | Reaches backward to a failure's root-cause memory | Zaki, Cai et al. 2024, *Nature* 637:145-155, [10.1038/s41586-024-08168-4](https://doi.org/10.1038/s41586-024-08168-4) |
| Synaptic Tagging | Marks memories for later consolidation | Frey & Morris 1997, [10.1038/385533a0](https://doi.org/10.1038/385533a0) |
| Spreading Activation | Retrieving one memory activates related ones through the graph | Collins & Loftus 1975, [10.1037/0033-295X.82.6.407](https://doi.org/10.1037/0033-295X.82.6.407) |
| Dual-Strength | Separates how well something is stored from how easily it is retrieved | Bjork & Bjork 1992 |
| Memory Dreaming | Sleep-like consolidation that replays and synthesizes memories | Sleep consolidation and replay |
| Active Forgetting | Top-down inhibition that suppresses a memory, cascades to neighbors, reversible for 24 hours | Anderson 2025, Davis 2020 |
---
## The 13 tools
Vestige exposes exactly 13 MCP tools. Your agent calls them; you rarely call them by hand.
| Tool | Purpose |
|---|---|
| `recall` | Retrieve memories relevant to the current context |
| `backfill` | Reach backward from a failure to its root-cause memory |
| `smart_ingest` | Store a fact, with gating for novelty and contradiction |
| `memory` | Read, inspect, promote, or demote individual memories |
| `graph` | Explore the memory graph and its links |
| `maintain` | Run consolidation and lifecycle maintenance |
| `dedup` | Find and merge duplicate memories |
| `suppress` | Actively forget a memory (reversible for 24h) |
| `memory_status` | Report health, counts, and model readiness |
| `codebase` | Index and query codebase-scoped memory |
| `intention` | Track goals and open intentions across sessions |
| `source_sync` | Sync memories from external connected sources |
| `session_start` | Prime the agent with relevant context at session start |
---
## The dashboard
## CLI
```bash
vestige dashboard
vestige stats # Memory statistics
vestige stats --tagging # Retention distribution
vestige stats --states # Cognitive state breakdown
vestige health # System health check
vestige consolidate # Run memory maintenance
vestige restore <file> # Restore from backup
vestige portable-export <file> # Exact cross-device archive
vestige portable-import <file> # Import archive into an empty database
vestige portable-import <file> --merge # Merge archive into this database
vestige sync <file> # Pull/merge/push via file backend
vestige dashboard # Open 3D dashboard in browser
```
Open **http://localhost:3927/dashboard** to watch your memory as a live 3D graph.
---
It is built with SvelteKit 2 and Svelte 5, rendering with WebGPU and Three.js with bloom, driven by a live WebSocket feed, holding 1000+ nodes at 60fps. Memories appear, link, strengthen, and fade in real time as your agent works. It installs as a PWA if you want it as a standalone app.
## Documentation
| Document | Contents |
|----------|----------|
| [FAQ](docs/FAQ.md) | 30+ common questions answered |
| [Science](docs/SCIENCE.md) | The neuroscience behind every feature |
| [Storage Modes](docs/STORAGE.md) | Global, per-project, multi-instance |
| [CLAUDE.md Setup](docs/CLAUDE-SETUP.md) | Templates for proactive memory |
| [Configuration](docs/CONFIGURATION.md) | CLI commands, environment variables |
| [Integrations](docs/integrations/) | Codex, Xcode, Cursor, VS Code, JetBrains, Windsurf |
| [Changelog](CHANGELOG.md) | Version history |
---
## Works with every agent
## Troubleshooting
Vestige is a standard MCP server, so it works with any MCP-capable client. The universal config is all most agents need:
<details>
<summary>"Command not found" after installation</summary>
```json
{
"mcpServers": {
"vestige": {
"command": "vestige-mcp"
}
}
}
Ensure `vestige-mcp` is in your PATH:
```bash
which vestige-mcp
# Or use the full path:
claude mcp add vestige /usr/local/bin/vestige-mcp -s user
```
</details>
<details>
<summary>Embedding model download fails</summary>
First run downloads ~130MB from Hugging Face. If behind a proxy:
```bash
export HTTPS_PROXY=your-proxy:port
```
| Client | Setup |
|---|---|
| Claude Code | `claude mcp add vestige vestige-mcp -s user` |
| Codex | `codex mcp add vestige -- vestige-mcp` |
| Cursor | [docs/integrations/cursor.md](docs/integrations/cursor.md) |
| VS Code | [docs/integrations/vscode.md](docs/integrations/vscode.md) |
| Windsurf | [docs/integrations/windsurf.md](docs/integrations/windsurf.md) |
| Claude Desktop | [docs/CONFIGURATION.md#claude-desktop-macos](docs/CONFIGURATION.md#claude-desktop-macos) |
| Cline / Continue / Zed / Goose | add the universal config above |
Cache: platform user cache directory first, then `./.fastembed_cache` as a fallback. Override with `FASTEMBED_CACHE_PATH`.
</details>
Full configuration reference: [docs/CONFIGURATION.md](docs/CONFIGURATION.md). Intel Mac notes: [docs/INSTALL-INTEL-MAC.md](docs/INSTALL-INTEL-MAC.md).
<details>
<summary>Dashboard not loading</summary>
Run `vestige dashboard` or set `VESTIGE_DASHBOARD_ENABLED=true`, then check:
```bash
curl http://localhost:3927/api/health
# Should return {"status":"healthy",...}
```
</details>
[More troubleshooting ->](docs/FAQ.md#troubleshooting)
---
## Optional: make the agent use memory automatically
## Contributing
By default your agent calls the tools when it decides to. If you want memory to be a standing habit (recall at the start of a task, save durable facts as they land), give the agent a short protocol.
Issues and PRs welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).
- General agent memory protocol: [docs/AGENT-MEMORY-PROTOCOL.md](docs/AGENT-MEMORY-PROTOCOL.md)
- Claude-specific setup and templates: [docs/CLAUDE-SETUP.md](docs/CLAUDE-SETUP.md)
## License
This is opt-in. Vestige works fine with no protocol at all.
AGPL-3.0 — free to use, modify, and self-host. If you offer Vestige as a network service, you must open-source your modifications.
---
## Under the hood
Vestige is a single Rust binary. No sidecar services, no external database, no cloud dependency.
| Component | Detail |
|---|---|
| Language | Rust 2024 edition, about 96,000 lines |
| Distribution | Single 25MB binary, prebuilt for all platforms |
| Embeddings | Nomic Embed Text v1.5 (768d reduced to 256d via Matryoshka, 8192-token context) |
| Reranker | Qwen3 reranker, optional |
| Vector search | USearch HNSW |
| Storage | SQLite with FTS5, optional SQLCipher encryption |
| First run | Downloads about 130MB embedding model once, then fully offline forever |
| Platforms | macOS (ARM + Intel), Linux x86_64, Windows x86_64, all prebuilt |
| Quality | 1,550 tests passing, clippy clean with `-D warnings` |
Storage internals and encryption: [docs/STORAGE.md](docs/STORAGE.md).
---
## Go deeper
| Doc | What's in it |
|---|---|
| [Getting Started](docs/GETTING-STARTED.md) | Full first-run walkthrough |
| [FAQ](docs/FAQ.md) | Common questions |
| [The Science](docs/SCIENCE.md) | Every mechanism with its citation |
| [Configuration](docs/CONFIGURATION.md) | All options and per-agent setup |
| [Storage](docs/STORAGE.md) | Storage format and encryption |
| [Agent Memory Protocol](docs/AGENT-MEMORY-PROTOCOL.md) | Teaching an agent to use memory automatically |
| [Intel Mac install](docs/INSTALL-INTEL-MAC.md) | Notes for older Macs |
| [Silent Rotation](https://github.com/samvallad33/vestige/tree/benchmark/silent-rotation/benchmarks/silent-rotation) | The reproducible benchmark |
| [Changelog](CHANGELOG.md) | Release history |
---
If Vestige saves you from one repeated mistake, that is the whole point: never solve the same problem twice. If it earns a place in your setup, [star it on GitHub](https://github.com/samvallad33/vestige). It genuinely helps me keep building.
Built by [Sam](https://github.com/samvallad33). Licensed under [AGPL-3.0](LICENSE).
<p align="center">
<i>Built by <a href="https://github.com/samvallad33">@samvallad33</a></i><br>
<sub>80,000+ lines of Rust · 30 cognitive modules · 130 years of memory research · one 22MB binary</sub>
</p>

View file

@ -1,627 +0,0 @@
# Vestige Cognitive Observatory WebGPU Implementation Spec
Status: RESEARCH -> SPEC ONLY. Do not implement app code from this file until Sam explicitly asks.
Goal: add a new full-bleed Cognitive Observatory route that reuses the existing graph/cinema/effects substrate, then layers a GPU-resident WebGPU simulation path for viral demo moments. Priority #1 is `?demo=recall-path`: a deterministic loop where recall lights a causal path through real memory nodes.
Ground truth read before this spec:
- `apps/dashboard/src/lib/components/Graph3D.svelte` currently owns the Three/WebGL scene, 60fps governor, `ForceSimulation`, `NodeManager`, `EdgeManager`, `ParticleSystem`, `EffectManager`, DreamMode, post-processing, and event mapping.
- `apps/dashboard/src/lib/graph/force-sim.ts` is a 101-line CPU simulation with O(N^2) repulsion, edge attraction, damping, centering, and frame-count cooldown.
- `apps/dashboard/src/lib/graph/scene.ts` is Three/WebGL `WebGLRenderer` + `EffectComposer`, not WebGPU.
- `apps/dashboard/src/lib/graph/particles.ts` and `effects.ts` still use CPU-updated `THREE.BufferGeometry` attributes and `Math.random()` bursts.
- `apps/dashboard/src/lib/graph/cinema/pathfinder.ts` is already deterministic and produces story beats/flow edges. Reuse this for recall paths.
- `apps/dashboard/src/lib/graph/cinema/director.ts` is renderer-agnostic camera choreography. Reuse its path semantics, not its renderer.
- `apps/dashboard/src/routes/(app)/graph/+page.svelte` has protected `MemoryCinema`; do not modify it. Add a separate route.
## 1. Exact WebGPU technique chosen per demo moment
### Shared baseline: GPU-resident node/particle state
Chosen technique: WebGPU Samples compute-boids ping-pong storage-buffer update + instanced sprite render.
Why: it is the cleanest canonical WebGPU pattern: node/particle state is in GPU buffers created with `GPUBufferUsage.VERTEX | GPUBufferUsage.STORAGE`; a compute pass writes next state; a render pass reads the current/next state as an instance vertex buffer and draws one tiny sprite mesh per node/particle. The sample uses `@compute @workgroup_size(64)` and dispatches `Math.ceil(numParticles / 64)` workgroups.
Sources:
- https://webgpu.github.io/webgpu-samples/samples/computeBoids/
- https://raw.githubusercontent.com/webgpu/webgpu-samples/main/sample/computeBoids/main.ts
- https://raw.githubusercontent.com/webgpu/webgpu-samples/main/sample/computeBoids/updateSprites.wgsl
- https://raw.githubusercontent.com/webgpu/webgpu-samples/main/sample/computeBoids/sprite.wgsl
- https://webgpufundamentals.org/webgpu/lessons/webgpu-storage-buffers.html
- https://webgpufundamentals.org/webgpu/lessons/webgpu-compute-shaders.html
Concrete pattern to use:
- `NodeState` storage buffer, 16-byte aligned lanes:
- `pos_radius: vec4f` = xyz + visual radius
- `vel_retention: vec4f` = xyz velocity + FSRS retention
- `color_flags: vec4f` = rgb + packed visual flags as f32 for MVP; move to u32 later if needed
- `demo: vec4f` = path phase, birth phase, ripple phase, shock phase
- Two ping-pong `GPUBuffer`s for simulation: previous read-only, next read-write.
- One static `EdgeIndex` storage buffer: `array<vec2u>` source/target indices.
- One `PathStep` storage buffer for demo path: `array<vec4u>` with source index, target index, beat frame, kind.
- One uniform buffer per frame: frame index, fixed time, loop phase, node count, edge count, path count, viewport, camera matrices.
- Compute workgroup size: start at 64 for broad compatibility; allow 128 only after profiling.
- Every WGSL entry checks `if (id.x >= params.nodeCount) { return; }` because dispatch is rounded up.
- CPU writes graph data once on route load, then only writes uniforms and demo controls. No per-frame readback.
### Moment A: `?demo=recall-path` — PATH lighting through nodes
Chosen technique: GPU path-wave scalar + edge glow render from the `PathStep` buffer.
Implementation:
- Use existing `planCinemaPath(nodes, edges, centerId, maxBeats)` to get deterministic beats and `flowEdges`.
- Convert each beat/edge to node indices and upload to `PathStep`.
- Compute pass `recallPathPass` runs per node. For each node, scan the small path buffer (max 7-12 beats) and compute:
- `beatT = smoothstep(beatFrame - 18, beatFrame + 18, frameInLoop)`
- `decayT = 1.0 - smoothstep(beatFrame + 45, beatFrame + 180, frameInLoop)`
- path intensity = max of beat arrival envelope for matching node index.
- Edge render reads source/target node states and path steps. A second `PathEdgeInstance` buffer is optional for MVP; simpler first build draws only path edges as instanced line quads using the `PathStep` source/target index.
- Color: cyan-white core with violet afterglow. Node `demo.x` = recall intensity. Edge alpha = wavefront envelope.
- Render order: background -> non-path edges -> nodes -> path edges additive -> path node halos additive -> DOM overlay.
Sources:
- WebGPU boids storage/render pattern above.
- `apps/dashboard/src/lib/graph/cinema/pathfinder.ts` for deterministic story path and flow edges.
- GraphWaGu edge vertex shader pattern, where the vertex shader fetches node positions from a storage buffer by source/target edge indices: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/edge_vert.wgsl
### Moment B: node BORN — particle convergence into a node
Chosen technique: GPU particle attractor convergence with deterministic seed, not CPU `Math.random()` bursts.
Implementation:
- Add a `BirthParticle` storage buffer sized for e.g. 16k particles shared across all births.
- For each birth event or demo beat, assign a deterministic particle slice: start positions are generated from `hash(seed, particleIndex, nodeIndex)` on GPU or precomputed once on CPU with the same seeded PRNG.
- Compute pass lerps particle position from shell/noise field toward target node position:
- `p = mix(start, target + curlNoiseOffset, easeOutCubic(t))`
- Fade from violet dust to node color; size shrinks on arrival.
- For MVP, render as instanced billboards. Later upgrade to screen-space point splatting if density gets high.
Sources:
- WebGPU Samples compute-boids for GPU particle update/render.
- Particle Life WebGPU for finite-radius particle update discipline and atomics when interactions become local-neighborhood based: https://lisyarus.github.io/blog/posts/particle-life-simulation-in-browser-using-webgpu.html
- Codrops WebGPU fluids on particle simulation and screen-space point-splatting as the real-time surface path: https://tympanus.net/codrops/2025/01/29/particles-progress-and-perseverance-a-journey-into-webgpu-fluids/
### Moment C: backward RIPPLE — radial distortion moving from effect to cause
Chosen technique: screen-space radial distortion post pass + node intensity ring.
Implementation:
- Add a fullscreen post-process pipeline after node/edge render.
- Uniform `Ripple { originClip: vec2f, radius: f32, width: f32, strength: f32, direction: f32 }`.
- Fragment shader samples the scene texture with UV displaced along radial direction:
- `d = distance(uv, originClip)`
- `ring = smoothstep(radius + width, radius, d) * smoothstep(radius - width, radius, d)`
- `uv2 = uv + normalize(uv - originClip) * ring * strength * direction`
- For backward causal recall, `direction = -1` and radius contracts from the failure node toward the cause node, while `PathStep` beats are ordered effect -> cause.
Sources:
- WebGPU Fundamentals render/post basics: https://webgpufundamentals.org/webgpu/lessons/webgpu-compute-shaders.html
- Codrops Shader.se article for scroll/timeline-driven WebGPU scene transitions and shader masking: https://tympanus.net/codrops/2026/05/19/80s-business-tech-seamless-scene-transitions-inside-shader-ses-scroll-driven-webgpu-pipeline/
- Existing `apps/dashboard/src/lib/graph/effects.ts` already has CPU `createRippleWave`; reuse semantics, move rendering to post shader for Observatory.
### Moment D: DRIFT toward a horizon — depth fog and horizon attractor
Chosen technique: GPU drift vector + depth/alpha fog in vertex/fragment shader.
Implementation:
- Add per-node `lifecycle` scalar from retention/state: active=near, dormant=mid, silent=far, unavailable=horizon.
- Compute pass applies a weak drift acceleration toward `horizon = vec3(0, -20, -220)` for low-retention nodes; high-retention nodes resist drift.
- Vertex shader writes depth/fog factor from camera-space z and lifecycle.
- Fragment shader mixes node color toward deep blue/black and reduces alpha with `smoothstep(fogNear, fogFar, depth)`.
Sources:
- Existing `scene.ts` uses Three `FogExp2`; Observatory should port the semantic to WGSL rather than rely on Three.
- Codrops False Earth for large WebGPU procedural world using compute shaders + indirect drawing; use as proof that compute-generated scene density belongs on GPU: https://tympanus.net/codrops/2026/04/21/false-earth-from-webgl-limits-to-a-webgpu-driven-world/
### Moment E: crimson SHOCKWAVE + membrane — firewall / immune response
Chosen technique: expanding instanced ring/membrane mesh + fullscreen chromatic shock pass.
Implementation:
- Keep an `EventPulse` uniform/storage ring buffer with origin, start frame, color, type.
- Node pass reads pulses and adds red rim lighting when `abs(distance(worldPos, origin) - radius) < width`.
- Membrane pass draws a camera-facing ring/quad sphere impostor with fresnel alpha, crimson core, black outer edge.
- Post pass applies a one-frame high-strength radial distortion and red channel bias when shockfront crosses screen-space pixel.
Sources:
- Existing `effects.ts:createShockwave` uses a CPU Three `RingGeometry`; reuse the visual grammar, move to WebGPU instanced membrane.
- Codrops WebGPU fluid articles for atomics/storage buffers/compute simulation made practical in browser: https://tympanus.net/codrops/2025/02/26/webgpu-fluid-simulations-high-performance-real-time-rendering/
- compute.toys gallery for WebGPU compute-shader idioms and GPU toy iteration: https://compute.toys/ and https://github.com/compute-toys/compute.toys
### GPU force-directed graph layout
Chosen technique for v1: GPU force layout with storage buffers, edge-local spring pass, approximate repulsion pass, and integrate pass. Start with exact O(N^2) repulsion for <=2k Observatory demo nodes; add Barnes-Hut / spatial bins before 10k+.
Why: existing `force-sim.ts` is O(N^2) CPU. The right migration is not to immediately rebuild all GraphWaGu complexity; it is to preserve Vestige's force semantics on GPU with the boids ping-pong pattern, then graduate repulsion acceleration once visual requirements exceed a few thousand nodes.
Required compute passes:
1. `clearForcesPass`: one invocation per node, zero `force.xyz`.
2. `edgeSpringPass`: one invocation per edge. Read source/target node positions, compute attraction, accumulate into source/target force. MVP avoids float atomics by writing one force contribution per edge endpoint to `EdgeForce` and reducing per node in pass 3; advanced path uses atomic fixed-point accumulation.
3. `repulsionPass`: one invocation per node; loop all nodes for MVP. For 10k+, replace with GraphWaGu-style Barnes-Hut/Morton tree or Particle Life spatial bins.
4. `integratePass`: one invocation per node; apply centering, damping, max velocity, retention/lifecycle drift, and ping-pong write to next node buffer.
5. `boundsPass` optional: compute bounds for fit camera; do not block v1 on GPU readback.
What moves from `force-sim.ts`:
- `positions: Map<string, THREE.Vector3>` becomes CPU index map + GPU `NodeState` buffers.
- `velocities: Map<string, THREE.Vector3>` becomes `vel_retention.xyz` in `NodeState`.
- `repulsionStrength`, `attractionStrength`, `dampening`, `alpha`, `maxSteps` become uniform fields.
- `tick(edges)` becomes command encoding of the compute passes above.
- `addNode/removeNode/reset` become buffer rebuilds for v1; optimize incremental updates later.
Sources:
- GraphWaGu repo: https://github.com/harp-lab/GraphWaGu
- GraphWaGu force-directed TS with compute pipelines/buffers: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/webgpu/force_directed.ts
- GraphWaGu exact force WGSL: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/compute_forces.wgsl
- GraphWaGu Barnes-Hut force WGSL: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/compute_forcesBH.wgsl
- GraphWaGu apply-forces WGSL: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/apply_forces.wgsl
- GraphWaGu Morton/tree/radix sort: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/create_tree.wgsl and https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/radix_sort.wgsl
- cosmos.gl / Cosmograph GPU force graph: https://github.com/cosmograph-org/cosmos and https://cosmograph.app/
- cosmos.gl README says computations and drawing occur on GPU and targets hundreds of thousands of points/links: https://raw.githubusercontent.com/cosmograph-org/cosmos/main/README.md
## 2. Precise files to add/change
Add docs/spec only in this run:
- `apps/dashboard/OBSERVATORY-SPEC.md` — this file.
Implementation files for the Qwen coder later:
Create:
- `apps/dashboard/src/routes/(app)/observatory/+page.svelte`
- New route only. Do not touch protected `graph/+page.svelte` or `MemoryCinema` wiring.
- Fetch graph data via existing `api.graph` pattern from graph route.
- Parse `?demo=` and `?seed=` with `URLSearchParams`.
- Render full-bleed `ObservatoryCanvas` plus DOM overlay.
- `apps/dashboard/src/lib/components/ObservatoryCanvas.svelte`
- Svelte wrapper with `onMount`/`onDestroy`, canvas bind, ResizeObserver, WebGPU lifecycle.
- `apps/dashboard/src/lib/observatory/engine.ts`
- Owns adapter/device/context, pipelines, buffers, render loop, resize, dispose.
- `apps/dashboard/src/lib/observatory/types.ts`
- CPU-side `ObservatoryNode`, `ObservatoryEdge`, `DemoMode`, `DemoClock`, buffer layout constants.
- `apps/dashboard/src/lib/observatory/demo-clock.ts`
- Deterministic fixed-step clock, seeded PRNG, loop-frame math.
- `apps/dashboard/src/lib/observatory/graph-upload.ts`
- Convert `GraphNode[]`/`GraphEdge[]` to stable node index map, typed arrays, path steps from `planCinemaPath`.
- `apps/dashboard/src/lib/observatory/overlays/TelemetryStrip.svelte`
- `apps/dashboard/src/lib/observatory/overlays/CommandSurface.svelte`
- `apps/dashboard/src/lib/observatory/overlays/TimelineSpine.svelte`
- `apps/dashboard/src/lib/observatory/overlays/InspectorPanel.svelte`
- `apps/dashboard/src/lib/observatory/shaders/simulate.wgsl.ts`
- `apps/dashboard/src/lib/observatory/shaders/render-nodes.wgsl.ts`
- `apps/dashboard/src/lib/observatory/shaders/render-edges.wgsl.ts`
- `apps/dashboard/src/lib/observatory/shaders/render-path.wgsl.ts`
- `apps/dashboard/src/lib/observatory/shaders/post-ripple-shock.wgsl.ts`
- `apps/dashboard/src/lib/observatory/__tests__/demo-clock.test.ts`
- `apps/dashboard/src/lib/observatory/__tests__/graph-upload.test.ts`
Extend, do not rewrite:
- `apps/dashboard/src/lib/graph/cinema/pathfinder.ts`
- Export or reuse `planCinemaPath` as-is. If extra metadata is needed, add small pure helper `pathToIndexSteps(path, nodeIndexById)` in observatory layer, not inside Cinema.
- `apps/dashboard/src/lib/graph/nodes.ts`
- Reuse `getNodeColor`, `getMemoryState`, `AHAGRAPH_COLORS`. No renderer logic added here.
- `apps/dashboard/src/lib/graph/effects.ts`
- Do not move existing Graph3D effects. Use it as visual reference only.
- `apps/dashboard/src/lib/graph/force-sim.ts`
- Leave CPU simulation intact for Graph3D. Add no WebGPU imports here. Observatory gets its own GPU sim module.
Do not change:
- `apps/dashboard/src/routes/(app)/graph/+page.svelte` protected `MemoryCinema` block.
- `apps/dashboard/src/lib/components/MemoryCinema.svelte` unless Sam explicitly asks.
- `crates/`, `Cargo.toml`, backend APIs.
## 3. `?demo=recall-path` priority build instructions
### 3.1 Route/component structure
1. Create `routes/(app)/observatory/+page.svelte`.
2. On mount:
- Parse `demo = new URLSearchParams(window.location.search).get('demo') ?? 'recall-path'`.
- Parse `seed = ...get('seed') ?? 'vestige-observatory-v1'`.
- Fetch graph with `api.graph({ max_nodes: 300, depth: 3, sort: 'recent' })` for MVP.
- Pass `nodes`, `edges`, `centerId`, `demo`, `seed` into `<ObservatoryCanvas />`.
3. Render route as a full viewport stage:
- parent `class="fixed inset-0 overflow-hidden bg-[#03040a]"`.
- canvas layer absolute inset-0.
- overlay layer absolute inset-0 pointer-events-none.
- specific interactive controls use `pointer-events-auto`.
### 3.2 Deterministic demo clock
Create `demo-clock.ts`:
- `const FPS = 60`.
- `const LOOP_SECONDS = 12` for recall-path; `LOOP_FRAMES = 720`.
- `frame` is an integer, not derived from `Date.now()`.
- In normal interactive mode, rAF accumulates elapsed time and advances fixed steps. In demo mode, each rendered frame advances exactly one fixed frame, or uses `?frame=N` for capture.
- `phase = (frame % LOOP_FRAMES) / LOOP_FRAMES`.
- Use a tiny deterministic PRNG (`xmur3` seed hash + `mulberry32`) in local code. Do not add a dependency for seedrandom unless needed.
- Ban `Math.random()` and `performance.now()` from simulation state. `performance.now()` may schedule frames only; it must not decide positions/colors/path.
Source rationale:
- Fixed timestep pattern: https://gafferongames.com/post/fix_your_timestep/
- URL query parsing: https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
- Svelte lifecycle: https://svelte.dev/docs/svelte/lifecycle-hooks
### 3.3 Graph upload
Create `graph-upload.ts`:
1. Stable-sort nodes for deterministic indices:
- center node first if `id === centerId`.
- then descending `updatedAt || createdAt`.
- tie-break by `id.localeCompare`.
2. Build `nodeIndexById: Map<string, number>`.
3. Create `Float32Array nodeData` with 16 floats per node for alignment and future-proofing:
- 0..3: x, y, z, radius
- 4..7: vx, vy, vz, retention
- 8..11: r, g, b, flags
- 12..15: pathIntensity, birthPhase, ripplePhase, shockPhase
4. Initial positions:
- Reuse the golden-angle sphere logic from `NodeManager.createNodes`, but replace randomness with deterministic seed.
- Radius scales from node count: `baseR = clamp(24 + sqrt(n) * 1.2, 35, 140)`.
5. Create `Uint32Array edgeData` with 2 u32 per edge, skipping edges whose node IDs are absent.
6. Call `planCinemaPath(nodes, edges, centerId, 8)` and convert `flowEdges` to `PathStep[]`.
7. Path timing:
- beat 0 at frame 60.
- each next beat +60 frames.
- final 180 frames reserved for afterglow and loop reset.
### 3.4 WebGPU engine setup
Create `engine.ts`:
1. Feature detect:
- `if (!navigator.gpu) throw new Error('WebGPU unavailable')` and render a DOM fallback panel.
2. Request adapter/device.
3. Configure canvas:
- `format = navigator.gpu.getPreferredCanvasFormat()`.
- On resize, set `canvas.width = Math.min(clientWidth * devicePixelRatio, maxTextureDimension2D)` and same for height.
- Reconfigure context and recreate depth/scene textures.
4. Create buffers:
- `nodeA`, `nodeB`: `STORAGE | VERTEX | COPY_DST`.
- `edges`: `STORAGE | COPY_DST`.
- `pathSteps`: `STORAGE | COPY_DST`.
- `params`: `UNIFORM | COPY_DST`.
- sprite vertex buffer for a 2-triangle quad or 3-vertex triangle impostor.
5. Create pipelines:
- `simulatePipeline` with read nodeA/write nodeB.
- `renderEdgesPipeline` reads node current + edges.
- `renderNodesPipeline` uses instance index to fetch node state and draw billboard.
- `renderPathPipeline` draws path edges + additive halos.
- `postPipeline` optional in increment 5.
6. Frame loop command order for recall-path MVP:
- write params uniform for integer frame.
- compute simulate: nodeA -> nodeB.
- render scene using nodeB.
- swap nodeA/nodeB.
### 3.5 WGSL simulation for recall path
MVP `simulate.wgsl.ts`:
- Inputs: params, previous nodes, next nodes, edges, path steps.
- For each node:
- start with previous `pos`, `vel`.
- apply low-cost centering and edge spring only after the first MVP render works; first render may use static positions.
- compute path intensity by scanning `pathSteps`.
- compute horizon drift from retention.
- write next node state.
Pseudo-WGSL shape for Qwen:
```wgsl
struct NodeState {
pos_radius: vec4f,
vel_retention: vec4f,
color_flags: vec4f,
demo: vec4f,
};
struct Params {
frame: u32,
loopFrames: u32,
nodeCount: u32,
edgeCount: u32,
pathCount: u32,
dt: f32,
time: f32,
_pad: f32,
};
struct PathStep { source: u32, target: u32, beatFrame: u32, kind: u32 };
@group(0) @binding(0) var<uniform> params: Params;
@group(0) @binding(1) var<storage, read> prevNodes: array<NodeState>;
@group(0) @binding(2) var<storage, read_write> nextNodes: array<NodeState>;
@group(0) @binding(3) var<storage, read> path: array<PathStep>;
fn saturate(x: f32) -> f32 { return clamp(x, 0.0, 1.0); }
fn smoothPulse(frame: f32, beat: f32, attack: f32, release: f32) -> f32 {
let a = smoothstep(beat - attack, beat, frame);
let r = 1.0 - smoothstep(beat, beat + release, frame);
return a * r;
}
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) gid: vec3u) {
let i = gid.x;
if (i >= params.nodeCount) { return; }
var node = prevNodes[i];
let f = f32(params.frame % params.loopFrames);
var recall = 0.0;
for (var p = 0u; p < params.pathCount; p = p + 1u) {
let step = path[p];
if (step.source == i || step.target == i) {
recall = max(recall, smoothPulse(f, f32(step.beatFrame), 18.0, 150.0));
}
}
node.demo.x = recall;
nextNodes[i] = node;
}
```
### 3.6 Render approach
Nodes:
- Use instanced billboards. Vertex shader fetches `NodeState` by `@builtin(instance_index)`.
- Billboard size = `radius * (1.0 + recallIntensity * 2.0)`.
- Fragment shader radial alpha: `alpha = smoothstep(1.0, 0.0, length(localUv))`.
- Color = `mix(baseColor, vec3(0.8, 0.95, 1.0), recallIntensity)`.
- Additive glow pass can be same shader with bigger billboard and low alpha.
Edges:
- MVP: draw edges as line-list if available; better: instanced thin quads from source to target in screen space.
- For path edges, draw a separate additive path pass from `PathStep` only, with wavefront alpha.
- GraphWaGu source pattern fetches nodes in vertex shader by edge endpoint index; use that for WebGPU: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/edge_vert.wgsl
Path lighting:
- At beat frame, target node blooms first.
- During frames `beatFrame..beatFrame+45`, edge from source to target gets a traveling pulse:
- `edgeT = saturate((frame - beatFrame) / 45)`.
- Fragment local coordinate along edge `u`; alpha peak at `abs(u - edgeT) < 0.08`.
- Previous path nodes keep afterglow for 2-3 seconds.
### 3.7 DOM overlay
Overlay zones:
- Top telemetry strip: demo mode, seed, node/edge count, frame, fps estimate.
- Left command surface: demo selector buttons (`recall-path`, `birth`, `ripple`, `drift`, `firewall`) and copy demo URL.
- Bottom timeline spine: 720-frame loop with beat tick marks and current frame cursor.
- Right inspector: selected beat/node summary.
CSS pattern:
- Root stage: `position: fixed; inset: 0; isolation: isolate; overflow: hidden;`.
- Canvas: `position:absolute; inset:0; width:100%; height:100%; display:block; z-index:0;`.
- Overlay: `position:absolute; inset:0; z-index:10; pointer-events:none;`.
- Interactive overlay children: `pointer-events:auto;`.
- Avoid KPI cards. These are instruments: thin glass, monospaced labels, tick marks, command rail.
Sources:
- MDN `pointer-events`: https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events
- WebGPU canvas resizing: https://webgpufundamentals.org/webgpu/lessons/webgpu-resizing-the-canvas.html
- Svelte lifecycle hooks: https://svelte.dev/docs/svelte/lifecycle-hooks
## 4. Build task-list with verification increments
### Increment 1 — route shell and deterministic URL contract
Build:
- Add `routes/(app)/observatory/+page.svelte`.
- Add minimal overlay shell and fetch graph data.
- Parse `?demo=recall-path&seed=...` and display them.
Verify:
- Run `pnpm --filter @vestige/dashboard check`.
- Open `/observatory?demo=recall-path&seed=test`.
- See full-bleed dark stage, top telemetry strip, no KPI cards, no console errors.
### Increment 2 — deterministic clock tests
Build:
- Add `demo-clock.ts` and tests.
- Fixed 60fps loop, 720-frame period, seeded PRNG.
Verify:
- `pnpm --filter @vestige/dashboard test -- demo-clock`
- Same seed produces identical first 100 random values and frame phases.
- Different seed changes generated positions.
### Increment 3 — WebGPU canvas boots and clears
Build:
- Add `ObservatoryCanvas.svelte` and `engine.ts` minimal WebGPU init.
- Canvas clears to near-black and resizes with DPR clamp.
Verify:
- `/observatory?demo=recall-path` displays WebGPU canvas.
- Resize browser; canvas remains sharp and fills screen.
- If WebGPU unavailable, route shows a readable fallback instead of crashing.
### Increment 4 — upload graph and render static nodes
Build:
- Add `graph-upload.ts` and tests.
- Convert graph nodes into stable indexed `Float32Array`.
- Render node billboards from storage buffer; no simulation yet.
Verify:
- Test stable node ordering and edge filtering.
- Browser shows 100-300 nodes in deterministic positions.
- Reload same seed: identical node positions.
### Increment 5 — recall path buffer + node glow
Build:
- Reuse `planCinemaPath` to create path steps.
- Add compute pass that writes `demo.x` recall intensity.
- Node shader blooms active path nodes.
Verify:
- `/observatory?demo=recall-path&seed=A` loops every 12s.
- The same sequence lights the same nodes after reload.
- `?seed=B` changes layout but path timing remains stable.
### Increment 6 — path edge wavefront
Build:
- Add render-path pipeline drawing additive edge wave traveling source -> target.
- Timeline spine shows beat ticks matching wave arrival.
Verify:
- Wavefront visibly travels along each path edge.
- At beat arrival, the target node blooms.
- Loop reset is seamless: final afterglow fades before frame 0.
### Increment 7 — low-cost GPU force sim
Build:
- Add static edge attraction + centering + damping in compute.
- Keep repulsion disabled or low-count O(N^2) only for <=500 nodes.
Verify:
- Nodes subtly settle without CPU `ForceSimulation`.
- Performance remains smooth at 300 nodes.
- No CPU per-frame node position loops.
### Increment 8 — lifecycle effects one by one
Build/verify separately:
- Born convergence particles: trigger in demo and verify deterministic convergence.
- Backward ripple post pass: verify radial distortion travels effect -> cause.
- Horizon drift/fog: silent/unavailable nodes visibly drift/fade toward horizon.
- Firewall shockwave/membrane: crimson ring expands and fades without breaking path demo.
### Increment 9 — capture mode
Build:
- Add `?capture=1` behavior: hide cursor, freeze random seed, optional `?frame=N` exact-frame render.
- Add overlay copy button for canonical demo URL.
Verify:
- Same URL + frame renders identical screenshot.
- 12-second screen recording loops without discontinuity.
### Increment 10 — performance guardrails
Build:
- Add telemetry: CPU frame ms estimate, GPU timestamp query only if supported, node count, buffer mode.
- Cap default demo to 300 real nodes; support synthetic 10k only behind `?stress=10000`.
Verify:
- No `mapAsync` in the frame loop except delayed optional profiling.
- 300-node demo holds 60fps on Sam's M1 Max.
- Stress mode degrades gracefully and can be disabled by removing query param.
## 5. Source index
WebGPU core patterns:
- WebGPU Samples compute boids demo: https://webgpu.github.io/webgpu-samples/samples/computeBoids/
- compute-boids main TS: https://raw.githubusercontent.com/webgpu/webgpu-samples/main/sample/computeBoids/main.ts
- compute-boids update WGSL: https://raw.githubusercontent.com/webgpu/webgpu-samples/main/sample/computeBoids/updateSprites.wgsl
- compute-boids sprite WGSL: https://raw.githubusercontent.com/webgpu/webgpu-samples/main/sample/computeBoids/sprite.wgsl
- WebGPU storage buffers: https://webgpufundamentals.org/webgpu/lessons/webgpu-storage-buffers.html
- WebGPU compute shaders: https://webgpufundamentals.org/webgpu/lessons/webgpu-compute-shaders.html
- WebGPU canvas resize: https://webgpufundamentals.org/webgpu/lessons/webgpu-resizing-the-canvas.html
- MDN WebGPU API: https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API
Particle/lifecycle shader references:
- Particle Life WebGPU article: https://lisyarus.github.io/blog/posts/particle-life-simulation-in-browser-using-webgpu.html
- Particle Life demo: https://lisyarus.github.io/webgpu/particle-life.html
- compute.toys: https://compute.toys/
- compute.toys repo: https://github.com/compute-toys/compute.toys
- Codrops WebGPU fluids: https://tympanus.net/codrops/2025/02/26/webgpu-fluid-simulations-high-performance-real-time-rendering/
- Codrops WebGPU particles/fluids: https://tympanus.net/codrops/2025/01/29/particles-progress-and-perseverance-a-journey-into-webgpu-fluids/
- Codrops Shader.se WebGPU transitions: https://tympanus.net/codrops/2026/05/19/80s-business-tech-seamless-scene-transitions-inside-shader-ses-scroll-driven-webgpu-pipeline/
- Codrops False Earth WebGPU world: https://tympanus.net/codrops/2026/04/21/false-earth-from-webgl-limits-to-a-webgpu-driven-world/
GPU graph layout:
- GraphWaGu repo: https://github.com/harp-lab/GraphWaGu
- GraphWaGu force-directed TS: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/webgpu/force_directed.ts
- GraphWaGu exact forces: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/compute_forces.wgsl
- GraphWaGu Barnes-Hut forces: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/compute_forcesBH.wgsl
- GraphWaGu apply forces: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/apply_forces.wgsl
- GraphWaGu tree build: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/create_tree.wgsl
- GraphWaGu radix sort: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/radix_sort.wgsl
- GraphWaGu node vertex: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/node_vert.wgsl
- GraphWaGu edge vertex: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/edge_vert.wgsl
- cosmos.gl repo: https://github.com/cosmograph-org/cosmos
- cosmos.gl README: https://raw.githubusercontent.com/cosmograph-org/cosmos/main/README.md
- Cosmograph product: https://cosmograph.app/
Svelte/DOM/determinism:
- Svelte lifecycle hooks: https://svelte.dev/docs/svelte/lifecycle-hooks
- MDN URLSearchParams: https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
- MDN pointer-events: https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events
- Fixed timestep: https://gafferongames.com/post/fix_your_timestep/
- seedrandom reference if a dependency is later preferred over local PRNG: https://github.com/davidbau/seedrandom
## 6. Non-negotiable implementation constraints
- New Observatory route only. Do not mutate the protected Memory Cinema block in `graph/+page.svelte`.
- Keep existing Three/WebGL `Graph3D` intact. Observatory is a parallel WebGPU renderer, not a replacement in v1.
- Do not add dependencies for v1. Use browser WebGPU, Svelte, TypeScript, and existing `three` only if needed for matrix math; prefer small local math helpers.
- No `Math.random()` in demo-visible state. No `Date.now()`/`performance.now()` deciding simulation state.
- No GPU readback in the frame loop. Profiling readback must be delayed and optional.
- Every increment must render something verifiable before moving to the next effect.
---
## 7. VISUAL DNA — "from another dimension" (MANDATORY aesthetic contract)
Sam's directive (Jul 3 2026): the Observatory must be **mind-boggling** and look like it
**came from another dimension** — the same grammar as the WebGPU waitlist hero and
`docs/launch/causal-brain-demo.html`. This is NOT a dashboard skin. It is a living
tissue. Every increment below inherits this section. Generic = rejected.
### 7.1 The signature palette (fuse two systems — do not invent a third)
TWO real palettes already exist in-repo. The Observatory FUSES them:
- **Meaning layer — FSRS state (from `lib/graph/nodes.ts`, use verbatim):**
- active `#10b981` emerald · dormant `#f59e0b` amber · silent `#8b5cf6` violet · unavailable `#6b7280` slate
- aha `#FFD700` · confusion/failure `#EF4444` · guardrail `#9CA3AF`
- **Transcendence layer — iridescent thin-film (from `causal-brain-demo.html` `spectral(w)`):**
a catmull blend around 4 anchors, w∈[0,1]:
- indigo `vec3(0.20,0.28,0.95)` → cyan-teal `vec3(0.20,0.85,0.90)` → mint `vec3(0.45,1.00,0.72)` → magenta rim `vec3(0.85,0.45,1.00)`
- CORE reads indigo/teal (living tissue); MOTION shifts toward cyan/magenta; NEVER muddy orange.
- **Void:** background `#05060a` (near-black, `color-scheme: dark`). No grey dashboard chrome.
- **Rule:** a node's BASE hue = its FSRS state color. Its ACTIVATION glow (recall, birth,
ignite) rides the thin-film spectral band. So resting graph = meaningful; active graph = otherworldly.
Port `spectral(w)` to WGSL exactly (4-anchor catmull-ish blend). This is the single most
important visual primitive — it is what makes it look alien instead of corporate.
### 7.2 Glow / bloom language (copy the demo's exact values, then push further in WebGPU)
- Additive radial glow blobs (`glow(x,y,rad,col,alpha)` in the demo) → in WebGPU, a real
additive bloom pass. Node halos are `0 0 24px` soft; ignite events drop-shadow at
`0 0 26px rgba(110,240,220,.45)` energy.
- Text/instrument overlay glow: captions `text-shadow:0 0 24px rgba(30,180,255,.35)`;
verdict headline `linear-gradient(90deg,#7fe6c0,#6ef0e6,#a6dcff)` clipped to text with
`drop-shadow(0 0 26px rgba(110,240,220,.45))`; wordmark `letter-spacing:.28em`, `#5dcaa5`.
- **Breath:** a global `pulse = 0.5 + 0.5*sin(t*0.002)` (~0.32Hz) modulates halo intensity so
the whole field breathes even at rest. Living, never static.
### 7.3 DOM = instrument overlay ONLY (already the spec rule — here is the exact style)
Copy the demo's overlay contract: `.layer { position:fixed; inset:0; pointer-events:none }`
over a full-bleed `inset:0` canvas. Instruments (TelemetryStrip, CommandSurface,
TimelineSpine, InspectorPanel) are the ONLY DOM, styled like the demo's `.cap.mono`
(SF Mono, `#cfe9ff`), `.verdict` (radial-gradient vignette card), `.wordmark`. NO KPI cards.
NO solid panels. Instruments float, glow faintly, and never block the canvas
(`pointer-events:auto` only on the specific interactive control).
### 7.4 The reference moment already exists in 2D — port it, don't reinvent
`causal-brain-demo.html` IS Moment C (salience-rescue / backward-trace) as a 2D canvas
proof: brain point cloud (two lobes, center-dense), a failure flare on the deep right, a
vector-search stall on confounders, then a BACKWARD arrow tracing to the dormant cause on
the deep-left which **ignites**, then a verdict card ("Vestige 60% · vector search 0%"),
then the VESTIGE wordmark. Beat clock: brain forms 0-1.5s, fail fades in ~0.3s, vector
stalls 4-6.5s, backward trace fires 6.5-10.5s, verdict 10.5-13s, signature 13-15s, loop.
The Observatory's `?demo=salience-rescue` must reproduce this exact narrative in real
WebGPU with real memory nodes. Study its `spectral`, `seedBrain`, `glow`, and beat
`schedule` before writing the WGSL.
### 7.5 Non-negotiable "another dimension" checklist (verifier MUST confirm each)
- [ ] Field breathes at rest (global pulse), never a static image.
- [ ] Node base hue = FSRS state; activation = thin-film spectral. Both visible.
- [ ] Real additive bloom on ignite/recall (not just opacity).
- [ ] Void `#05060a`, zero grey dashboard chrome, zero KPI cards.
- [ ] Instruments are glowing SF-Mono overlays with `pointer-events:none` except controls.
- [ ] `?demo=salience-rescue` reproduces the causal-brain backward-trace narrative.
- [ ] It looks like living tissue from another dimension, not a SaaS dashboard. If a
reviewer would call it "clean" or "professional," it FAILED. The word is "alive."

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 +0,0 @@
@keyframes svelte-1n9p0vm-fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.animate-fade-in.svelte-1n9p0vm{animation:svelte-1n9p0vm-fade-in .3s ease-out}.live-status.svelte-1o4vd58{display:inline-flex;align-items:center;gap:.5rem;padding:.3rem .7rem;border-radius:999px;font-size:.75rem;font-weight:600;color:var(--color-recall, #34d399);background:color-mix(in srgb,var(--color-recall, #34d399) 12%,transparent);border:1px solid color-mix(in srgb,var(--color-recall, #34d399) 28%,transparent);white-space:nowrap}.live-status.idle.svelte-1o4vd58{color:var(--color-dim, #8b95a5);background:#ffffff0a;border-color:#ffffff14}.live-dot.svelte-1o4vd58{display:inline-block;flex-shrink:0}.count-chip.svelte-1o4vd58{display:inline-flex;align-items:baseline;gap:.35rem;padding:.3rem .7rem;border-radius:999px;font-size:.78rem;background:#ffffff08;border:1px solid rgba(255,255,255,.07)}.clear-btn.svelte-1o4vd58{display:inline-flex;align-items:center;gap:.3rem;padding:.3rem .65rem;border-radius:999px;font-size:.75rem;color:var(--color-muted, #6b7280);background:#ffffff05;border:1px solid rgba(255,255,255,.06);transition:color .2s ease,background .2s ease,border-color .2s ease}.clear-btn.svelte-1o4vd58:hover:not(:disabled){color:var(--color-text, #e5e7eb);background:#ffffff0d;border-color:#ffffff1f}.clear-btn.svelte-1o4vd58:disabled{opacity:.4;cursor:not-allowed}.event-row.svelte-1o4vd58{position:relative;transition:transform .28s cubic-bezier(.34,1.56,.64,1),box-shadow .28s ease,background .2s ease}.event-row.svelte-1o4vd58:hover{background:color-mix(in srgb,var(--evt) 7%,transparent)}.event-icon.svelte-1o4vd58{width:1.6rem;height:1.6rem;border-radius:.5rem}.empty-glyph.svelte-1o4vd58{display:inline-flex;align-items:center;justify-content:center;color:var(--color-synapse-glow, #818cf8);opacity:.85}@media not (prefers-reduced-motion:reduce){.empty-glyph.svelte-1o4vd58{animation:breathe 3.2s ease-in-out infinite}}

View file

@ -1 +0,0 @@
.cinema-overlay.svelte-1uwqs3k{position:fixed;top:0;right:0;bottom:0;left:0;z-index:200;background:radial-gradient(circle at 50% 40%,#05050f,#010108);display:flex;flex-direction:column}body.cinema-open .graph-stats-pill{display:none}.cinema-canvas.svelte-1uwqs3k{position:absolute;top:0;right:0;bottom:0;left:0;z-index:0}.cinema-top.svelte-1uwqs3k{position:relative;z-index:2;display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:max(.75rem,env(safe-area-inset-top)) 1rem .75rem;flex-wrap:wrap}.cinema-badge.svelte-1uwqs3k{font-size:.65rem;padding:.1rem .45rem;border-radius:999px;border:1px solid rgba(129,140,248,.4);color:var(--color-synapse-glow)}.cinema-badge-gpu.svelte-1uwqs3k{border-color:#14e8c680;color:#14e8c6}.cinema-act.svelte-1uwqs3k{font-size:.6rem;letter-spacing:.12em;text-transform:uppercase;color:var(--color-dream-glow);opacity:.85}.cinema-plan-card.svelte-1uwqs3k{position:absolute;z-index:3;top:50%;left:50%;transform:translate(-50%,-50%);max-width:520px;padding:1.5rem 1.75rem;border-radius:16px;text-align:center;animation:svelte-1uwqs3k-cinema-plan-in .5s ease both}@keyframes svelte-1uwqs3k-cinema-plan-in{0%{opacity:0;transform:translate(-50%,-46%)}to{opacity:1;transform:translate(-50%,-50%)}}.cinema-plan-kicker.svelte-1uwqs3k{font-size:.65rem;letter-spacing:.18em;text-transform:uppercase;color:var(--color-synapse-glow);margin-bottom:.5rem}.cinema-plan-logline.svelte-1uwqs3k{font-size:clamp(1.05rem,2.2vw,1.4rem);line-height:1.5;color:var(--color-bright);margin:0}.cinema-note.svelte-1uwqs3k{font-size:.78rem;color:var(--color-synapse-glow);opacity:.85;margin:0 0 .6rem;font-style:italic}.cinema-dot.svelte-1uwqs3k{width:8px;height:8px;border-radius:50%;background:var(--color-muted)}.cinema-dot.active.svelte-1uwqs3k{background:#14e8c6;box-shadow:0 0 10px #14e8c6}.cinema-toggle.svelte-1uwqs3k{font-size:.7rem;color:var(--color-dim);display:flex;align-items:center;gap:.3rem;cursor:pointer}.cinema-close.svelte-1uwqs3k{background:transparent;border:1px solid rgba(255,255,255,.15);color:var(--color-text);border-radius:8px;width:32px;height:32px;cursor:pointer}.cinema-caption-wrap.svelte-1uwqs3k{position:relative;z-index:2;margin-top:auto;padding:1rem 1.25rem max(1.25rem,env(safe-area-inset-bottom));max-width:720px}.cinema-chip.svelte-1uwqs3k{display:inline-block;font-size:.65rem;text-transform:uppercase;letter-spacing:.08em;color:var(--color-dream-glow);margin-bottom:.35rem}.cinema-caption.svelte-1uwqs3k{font-size:clamp(1.05rem,2.4vw,1.6rem);line-height:1.45;color:var(--color-bright);text-shadow:0 2px 24px rgba(0,0,0,.9);min-height:2.6em;margin:0 0 .75rem}.cinema-progress.svelte-1uwqs3k{height:3px;background:#ffffff1a;border-radius:3px;overflow:hidden}.cinema-progress-fill.svelte-1uwqs3k{height:100%;background:linear-gradient(90deg,var(--color-synapse),color-mix(in oklch,var(--color-dream),#ff2d55 calc(var(--tension, 0) * 100%)));transition:width .2s linear,background .4s ease}.cinema-beatcount.svelte-1uwqs3k{margin-top:.4rem;display:flex;gap:.75rem;align-items:center}.cinema-replay.svelte-1uwqs3k{background:transparent;border:1px solid rgba(129,140,248,.4);color:var(--color-synapse-glow);border-radius:999px;padding:.15rem .7rem;cursor:pointer;font-size:.75rem}.cinema-dream.svelte-1uwqs3k{color:var(--color-dream-glow);letter-spacing:.08em;animation:svelte-1uwqs3k-cinema-dream-pulse 3s ease-in-out infinite}.cinema-restore.svelte-1uwqs3k{position:absolute;bottom:.6rem;right:.8rem;z-index:95;background:#0a0a1a80;border:1px solid rgba(129,140,248,.25);color:var(--color-muted);border-radius:999px;padding:.2rem .7rem;font-size:.7rem;letter-spacing:.04em;cursor:pointer;opacity:.4;transition:opacity .2s ease}.cinema-restore.svelte-1uwqs3k:hover{opacity:1}@keyframes svelte-1uwqs3k-cinema-dream-pulse{0%,to{opacity:.55}50%{opacity:1}}@media(prefers-reduced-motion:reduce){.cinema-progress-fill.svelte-1uwqs3k{transition:none}}

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
.conn-pill.svelte-15kgmsr{display:inline-flex;align-items:center;gap:.5rem;padding:.3rem .7rem;border-radius:999px;font-size:.75rem;font-weight:600;color:var(--color-recall, #34d399);background:color-mix(in srgb,var(--color-recall, #34d399) 12%,transparent);border:1px solid color-mix(in srgb,var(--color-recall, #34d399) 28%,transparent);white-space:nowrap}.conn-pill.idle.svelte-15kgmsr{color:var(--color-dim, #8b95a5);background:#ffffff0a;border-color:#ffffff14}.conn-dot.svelte-15kgmsr{display:inline-block;flex-shrink:0}.refresh-btn.svelte-15kgmsr{display:inline-flex;align-items:center;gap:.3rem;padding:.3rem .65rem;border-radius:999px;font-size:.75rem;color:var(--color-muted, #6b7280);background:#ffffff05;border:1px solid rgba(255,255,255,.06);transition:color .2s ease,background .2s ease,border-color .2s ease}.refresh-btn.svelte-15kgmsr:hover{color:var(--color-text, #e5e7eb);background:#ffffff0d;border-color:#ffffff1f}.logo-tile.svelte-15kgmsr{position:relative}.logo-tile.svelte-15kgmsr:after{content:"";position:absolute;top:-1px;right:-1px;bottom:-1px;left:-1px;border-radius:inherit;box-shadow:0 0 18px -2px var(--color-synapse-glow, #818cf8);opacity:.4;pointer-events:none}@media not (prefers-reduced-motion:reduce){.logo-tile.svelte-15kgmsr:after{animation:svelte-15kgmsr-logo-glow 4s ease-in-out infinite}@keyframes svelte-15kgmsr-logo-glow{0%,to{opacity:.25}50%{opacity:.55}}}

View file

@ -1 +0,0 @@
.metric-card.svelte-w41m0t{cursor:default}

View file

@ -1,2 +0,0 @@
€.metric-card.svelte-w41m0t{cursor:default}


File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
@keyframes svelte-1uyjqky-fadeSlide{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}

View file

@ -0,0 +1 @@
@keyframes svelte-1n9p0vm-fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.animate-fade-in.svelte-1n9p0vm{animation:svelte-1n9p0vm-fade-in .3s ease-out}

View file

@ -1 +0,0 @@
.dd.svelte-1fd3ybn{position:relative;display:inline-flex;flex-direction:column;gap:.3rem}.dd-label.svelte-1fd3ybn{font-size:.65rem;text-transform:uppercase;letter-spacing:.08em;color:var(--color-muted);padding-left:.15rem}.dd-trigger.svelte-1fd3ybn{display:inline-flex;align-items:center;gap:.5rem;padding:.55rem .7rem .55rem .8rem;min-width:9rem;background:#ffffff08;border:1px solid rgba(99,102,241,.12);border-radius:.75rem;color:var(--color-text);font-size:.8rem;font-family:inherit;cursor:pointer;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);transition:border-color .2s ease,background .2s ease,box-shadow .2s ease}.dd-trigger.svelte-1fd3ybn:hover{border-color:#6366f14d;background:#ffffff0d}.dd-trigger.svelte-1fd3ybn:focus-visible,.dd-trigger.dd-open.svelte-1fd3ybn{outline:none;border-color:#6366f180;box-shadow:0 0 0 3px #6366f11f}.dd-trigger-icon.svelte-1fd3ybn{color:var(--color-synapse-glow);display:inline-flex}.dd-value.svelte-1fd3ybn{flex:1;text-align:left;display:inline-flex;align-items:center;gap:.45rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dd-placeholder.svelte-1fd3ybn{color:var(--color-muted)}.dd-dot.svelte-1fd3ybn{width:.5rem;height:.5rem;border-radius:50%;flex-shrink:0;box-shadow:0 0 6px currentColor}.dd-chevron.svelte-1fd3ybn{color:var(--color-dim);display:inline-flex;transition:transform .25s cubic-bezier(.34,1.56,.64,1)}.dd-chevron-open.svelte-1fd3ybn{transform:rotate(180deg);color:var(--color-synapse-glow)}.dd-menu.svelte-1fd3ybn{position:absolute;top:calc(100% + .4rem);left:0;z-index:60;min-width:100%;max-height:18rem;overflow-y:auto;padding:.35rem;border-radius:.85rem;transform-origin:top center}@media not (prefers-reduced-motion:reduce){.dd-menu.svelte-1fd3ybn{animation:svelte-1fd3ybn-dd-pop .18s cubic-bezier(.34,1.56,.64,1)}@keyframes svelte-1fd3ybn-dd-pop{0%{opacity:0;transform:translateY(-6px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}}.dd-option.svelte-1fd3ybn{width:100%;display:flex;align-items:center;gap:.5rem;padding:.5rem .6rem;border:none;background:transparent;color:var(--color-dim);font-size:.8rem;font-family:inherit;text-align:left;border-radius:.6rem;cursor:pointer;transition:background .12s ease,color .12s ease}.dd-active.svelte-1fd3ybn{background:#6366f124;color:var(--color-text)}.dd-selected.svelte-1fd3ybn{color:var(--color-synapse-glow)}.dd-opt-icon.svelte-1fd3ybn{color:var(--color-dim);display:inline-flex}.dd-active.svelte-1fd3ybn .dd-opt-icon:where(.svelte-1fd3ybn){color:var(--color-synapse-glow)}.dd-opt-label.svelte-1fd3ybn{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dd-badge.svelte-1fd3ybn{font-size:.65rem;font-variant-numeric:tabular-nums;color:var(--color-muted);background:#ffffff0d;padding:.05rem .4rem;border-radius:.4rem}.dd-check.svelte-1fd3ybn{color:var(--color-synapse-glow);display:inline-flex}

View file

@ -1 +0,0 @@
.vestige-icon.svelte-1eqehiz{display:inline-block;flex-shrink:0;vertical-align:middle;overflow:visible}@media not (prefers-reduced-motion:reduce){.vestige-icon-draw.svelte-1eqehiz path,.vestige-icon-draw.svelte-1eqehiz circle,.vestige-icon-draw.svelte-1eqehiz rect{stroke-dasharray:64;stroke-dashoffset:64;animation:svelte-1eqehiz-icon-draw .7s cubic-bezier(.65,0,.35,1) forwards}@keyframes svelte-1eqehiz-icon-draw{to{stroke-dashoffset:0}}}

View file

@ -1 +0,0 @@
.header-tile.svelte-162svzm:after{content:"";position:absolute;top:-1px;right:-1px;bottom:-1px;left:-1px;border-radius:inherit;box-shadow:0 0 18px -2px currentColor;opacity:.35;pointer-events:none}@media not (prefers-reduced-motion:reduce){.header-tile.svelte-162svzm:after{animation:svelte-162svzm-tile-glow 4s ease-in-out infinite}@keyframes svelte-162svzm-tile-glow{0%,to{opacity:.22}50%{opacity:.5}}}.text-balance.svelte-162svzm{text-wrap:balance}.text-pretty.svelte-162svzm{text-wrap:pretty}

View file

@ -0,0 +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};

View file

@ -1,2 +0,0 @@
import{D as z,q as dr,aD as sr,G as A,L as U,N as gr,T as pr,g as m,U as hr,W as _r,X as x,K,M as G,I as Er,aE as Ar,aF as y,w as Sr,aG as b,$ as q,aH as Tr,a1 as Nr,ao as br,z as Ir,aI as V,aJ as Mr,aK as Or,ac as Cr,aL as rr,aM as Rr,Y as ur,_ as or,aN as P,R as lr,aO as Lr,aP as wr,aQ as Hr,Z as kr,J as Dr,aR as Fr,aS as Gr,aT as zr,aU as Ur,aV as tr,aW as Kr}from"./wpu9U-D0.js";function jr(r,e){return e}function Pr(r,e,f){for(var a=[],n=e.length,s,u=e.length,v=0;v<n;v++){let g=e[v];or(g,()=>{if(s){if(s.pending.delete(g),s.done.add(g),s.pending.size===0){var t=r.outrogroups;B(V(s.done)),t.delete(s),t.size===0&&(r.outrogroups=null)}}else u-=1},!1)}if(u===0){var o=a.length===0&&f!==null;if(o){var d=f,l=d.parentNode;Hr(l),l.append(d),r.items.clear()}B(e,!o)}else s={pending:new Set(e),done:new Set},(r.outrogroups??(r.outrogroups=new Set)).add(s)}function B(r,e=!0){for(var f=0;f<r.length;f++)kr(r[f],e)}var er;function mr(r,e,f,a,n,s=null){var u=r,v=new Map,o=(e&sr)!==0;if(o){var d=r;u=A?U(gr(d)):d.appendChild(z())}A&&pr();var l=null,g=br(()=>{var c=f();return Ir(c)?c:c==null?[]:V(c)}),t,p=!0;function S(){i.fallback=l,Yr(i,t,u,e,a),l!==null&&(t.length===0?(l.f&b)===0?ur(l):(l.f^=b,k(l,null,u)):or(l,()=>{l=null}))}var T=dr(()=>{t=m(g);var c=t.length;let C=!1;if(A){var R=hr(u)===_r;R!==(c===0)&&(u=x(),U(u),K(!1),C=!0)}for(var _=new Set,M=Sr,L=Nr(),h=0;h<c;h+=1){A&&G.nodeType===Er&&G.data===Ar&&(u=G,C=!0,K(!1));var O=t[h],w=a(O,h),E=p?null:v.get(w);E?(E.v&&y(E.v,O),E.i&&y(E.i,h),L&&M.unskip_effect(E.e)):(E=qr(v,p?u:er??(er=z()),O,w,h,n,e,f),p||(E.e.f|=b),v.set(w,E)),_.add(w)}if(c===0&&s&&!l&&(p?l=q(()=>s(u)):(l=q(()=>s(er??(er=z()))),l.f|=b)),c>_.size&&Tr(),A&&c>0&&U(x()),!p)if(L){for(const[D,F]of v)_.has(D)||M.skip_effect(F.e);M.oncommit(S),M.ondiscard(()=>{})}else S();C&&K(!0),m(g)}),i={effect:T,items:v,outrogroups:null,fallback:l};p=!1,A&&(u=G)}function H(r){for(;r!==null&&(r.f&Lr)===0;)r=r.next;return r}function Yr(r,e,f,a,n){var E,D,F,X,J,W,$,Q,Z;var s=(a&wr)!==0,u=e.length,v=r.items,o=H(r.effect.first),d,l=null,g,t=[],p=[],S,T,i,c;if(s)for(c=0;c<u;c+=1)S=e[c],T=n(S,c),i=v.get(T).e,(i.f&b)===0&&((D=(E=i.nodes)==null?void 0:E.a)==null||D.measure(),(g??(g=new Set)).add(i));for(c=0;c<u;c+=1){if(S=e[c],T=n(S,c),i=v.get(T).e,r.outrogroups!==null)for(const N of r.outrogroups)N.pending.delete(i),N.done.delete(i);if((i.f&b)!==0)if(i.f^=b,i===o)k(i,null,f);else{var C=l?l.next:o;i===r.effect.last&&(r.effect.last=i.prev),i.prev&&(i.prev.next=i.next),i.next&&(i.next.prev=i.prev),I(r,l,i),I(r,i,C),k(i,C,f),l=i,t=[],p=[],o=H(l.next);continue}if((i.f&P)!==0&&(ur(i),s&&((X=(F=i.nodes)==null?void 0:F.a)==null||X.unfix(),(g??(g=new Set)).delete(i))),i!==o){if(d!==void 0&&d.has(i)){if(t.length<p.length){var R=p[0],_;l=R.prev;var M=t[0],L=t[t.length-1];for(_=0;_<t.length;_+=1)k(t[_],R,f);for(_=0;_<p.length;_+=1)d.delete(p[_]);I(r,M.prev,L.next),I(r,l,M),I(r,L,R),o=R,l=L,c-=1,t=[],p=[]}else d.delete(i),k(i,o,f),I(r,i.prev,i.next),I(r,i,l===null?r.effect.first:l.next),I(r,l,i),l=i;continue}for(t=[],p=[];o!==null&&o!==i;)(d??(d=new Set)).add(o),p.push(o),o=H(o.next);if(o===null)continue}(i.f&b)===0&&t.push(i),l=i,o=H(i.next)}if(r.outrogroups!==null){for(const N of r.outrogroups)N.pending.size===0&&(B(V(N.done)),(J=r.outrogroups)==null||J.delete(N));r.outrogroups.size===0&&(r.outrogroups=null)}if(o!==null||d!==void 0){var h=[];if(d!==void 0)for(i of d)(i.f&P)===0&&h.push(i);for(;o!==null;)(o.f&P)===0&&o!==r.fallback&&h.push(o),o=H(o.next);var O=h.length;if(O>0){var w=(a&sr)!==0&&u===0?f:null;if(s){for(c=0;c<O;c+=1)($=(W=h[c].nodes)==null?void 0:W.a)==null||$.measure();for(c=0;c<O;c+=1)(Z=(Q=h[c].nodes)==null?void 0:Q.a)==null||Z.fix()}Pr(r,h,w)}}s&&lr(()=>{var N,j;if(g!==void 0)for(i of g)(j=(N=i.nodes)==null?void 0:N.a)==null||j.apply()})}function qr(r,e,f,a,n,s,u,v){var o=(u&Mr)!==0?(u&Or)===0?Cr(f,!1,!1):rr(f):null,d=(u&Rr)!==0?rr(n):null;return{v:o,i:d,e:q(()=>(s(e,o??f,d??n,v),()=>{r.delete(a)}))}}function k(r,e,f){if(r.nodes)for(var a=r.nodes.start,n=r.nodes.end,s=e&&(e.f&b)===0?e.nodes.start:f;a!==null;){var u=Dr(a);if(s.before(a),a===n)return;a=u}}function I(r,e,f){e===null?r.effect.first=f:e.next=f,f===null?r.effect.last=e:f.prev=e}function cr(r){var e,f,a="";if(typeof r=="string"||typeof r=="number")a+=r;else if(typeof r=="object")if(Array.isArray(r)){var n=r.length;for(e=0;e<n;e++)r[e]&&(f=cr(r[e]))&&(a&&(a+=" "),a+=f)}else for(f in r)r[f]&&(a&&(a+=" "),a+=f);return a}function Br(){for(var r,e,f=0,a="",n=arguments.length;f<n;f++)(r=arguments[f])&&(e=cr(r))&&(a&&(a+=" "),a+=e);return a}function xr(r){return typeof r=="object"?Br(r):r??""}const fr=[...`
\r\f \v\uFEFF`];function Vr(r,e,f){var a=r==null?"":""+r;if(e&&(a=a?a+" "+e:e),f){for(var n of Object.keys(f))if(f[n])a=a?a+" "+n:n;else if(a.length)for(var s=n.length,u=0;(u=a.indexOf(n,u))>=0;){var v=u+s;(u===0||fr.includes(a[u-1]))&&(v===a.length||fr.includes(a[v]))?a=(u===0?"":a.substring(0,u))+a.substring(v+1):u=v}}return a===""?null:a}function ar(r,e=!1){var f=e?" !important;":";",a="";for(var n of Object.keys(r)){var s=r[n];s!=null&&s!==""&&(a+=" "+n+": "+s+f)}return a}function Y(r){return r[0]!=="-"||r[1]!=="-"?r.toLowerCase():r}function yr(r,e){if(e){var f="",a,n;if(Array.isArray(e)?(a=e[0],n=e[1]):a=e,r){r=String(r).replaceAll(/\s*\/\*.*?\*\/\s*/g,"").trim();var s=!1,u=0,v=!1,o=[];a&&o.push(...Object.keys(a).map(Y)),n&&o.push(...Object.keys(n).map(Y));var d=0,l=-1;const T=r.length;for(var g=0;g<T;g++){var t=r[g];if(v?t==="/"&&r[g-1]==="*"&&(v=!1):s?s===t&&(s=!1):t==="/"&&r[g+1]==="*"?v=!0:t==='"'||t==="'"?s=t:t==="("?u++:t===")"&&u--,!v&&s===!1&&u===0){if(t===":"&&l===-1)l=g;else if(t===";"||g===T-1){if(l!==-1){var p=Y(r.substring(d,l).trim());if(!o.includes(p)){t!==";"&&g++;var S=r.substring(d,g).trim();f+=" "+S+";"}}d=g+1,l=-1}}}}return a&&(f+=ar(a)),n&&(f+=ar(n,!0)),f=f.trim(),f===""?null:f}return r==null?null:String(r)}function re(r,e,f,a,n,s){var u=r.__className;if(A||u!==f||u===void 0){var v=Vr(f,a,s);(!A||v!==r.getAttribute("class"))&&(v==null?r.removeAttribute("class"):e?r.className=v:r.setAttribute("class",v)),r.__className=f}else if(s&&n!==s)for(var o in s){var d=!!s[o];(n==null||d!==!!n[o])&&r.classList.toggle(o,d)}return s}const Xr=Symbol("is custom element"),Jr=Symbol("is html"),Wr=tr?"link":"LINK",$r=tr?"progress":"PROGRESS";function ee(r){if(A){var e=!1,f=()=>{if(!e){if(e=!0,r.hasAttribute("value")){var a=r.value;ir(r,"value",null),r.value=a}if(r.hasAttribute("checked")){var n=r.checked;ir(r,"checked",null),r.checked=n}}};r.__on_r=f,lr(f),Kr()}}function fe(r,e){var f=vr(r);f.value===(f.value=e??void 0)||r.value===e&&(e!==0||r.nodeName!==$r)||(r.value=e??"")}function ir(r,e,f,a){var n=vr(r);A&&(n[e]=r.getAttribute(e),e==="src"||e==="srcset"||e==="href"&&r.nodeName===Wr)||n[e]!==(n[e]=f)&&(e==="loading"&&(r[Fr]=f),f==null?r.removeAttribute(e):typeof f!="string"&&Qr(r).includes(e)?r[e]=f:r.setAttribute(e,f))}function vr(r){return r.__attributes??(r.__attributes={[Xr]:r.nodeName.includes("-"),[Jr]:r.namespaceURI===Gr})}var nr=new Map;function Qr(r){var e=r.getAttribute("is")||r.nodeName,f=nr.get(e);if(f)return f;nr.set(e,f=[]);for(var a,n=r,s=Element.prototype;s!==n;){a=Ur(n);for(var u in a)a[u].set&&f.push(u);n=zr(n)}return f}export{ir as a,fe as b,xr as c,mr as e,jr as i,ee as r,re as s,yr as t};

View file

@ -0,0 +1 @@
import{a1 as u,a2 as v,a3 as h,N as i,a4 as g,a5 as f,Y as A,a6 as S}from"./CpWkWWOo.js";const N=Symbol("is custom element"),p=Symbol("is html"),T=f?"link":"LINK",E=f?"progress":"PROGRESS";function k(r){if(i){var s=!1,a=()=>{if(!s){if(s=!0,r.hasAttribute("value")){var e=r.value;_(r,"value",null),r.value=e}if(r.hasAttribute("checked")){var o=r.checked;_(r,"checked",null),r.checked=o}}};r.__on_r=a,A(a),S()}}function l(r,s){var a=d(r);a.value===(a.value=s??void 0)||r.value===s&&(s!==0||r.nodeName!==E)||(r.value=s??"")}function _(r,s,a,e){var o=d(r);i&&(o[s]=r.getAttribute(s),s==="src"||s==="srcset"||s==="href"&&r.nodeName===T)||o[s]!==(o[s]=a)&&(s==="loading"&&(r[u]=a),a==null?r.removeAttribute(s):typeof a!="string"&&L(r).includes(s)?r[s]=a:r.setAttribute(s,a))}function d(r){return r.__attributes??(r.__attributes={[N]:r.nodeName.includes("-"),[p]:r.namespaceURI===v})}var c=new Map;function L(r){var s=r.getAttribute("is")||r.nodeName,a=c.get(s);if(a)return a;c.set(s,a=[]);for(var e,o=r,n=Element.prototype;n!==o;){e=g(o);for(var t in e)e[t].set&&a.push(t);o=h(o)}return a}export{l as a,k as r,_ as s};

View file

@ -0,0 +1 @@
import{b as T,N as o,ab as b,E as h,ac as R,ax as p,ad as A,ae as E,T as g,R as l}from"./CpWkWWOo.js";import{B as v}from"./DdEqwvdI.js";function m(t,c,u=!1){o&&b();var n=new v(t),_=u?h:0;function i(a,r){if(o){const e=R(t);var s;if(e===p?s=0:e===A?s=!1:s=parseInt(e.substring(1)),a!==s){var f=E();g(f),n.anchor=f,l(!1),n.ensure(a,r),l(!0);return}}n.ensure(a,r)}T(()=>{var a=!1;c((r,s=0)=>{a=!0,i(s,r)}),a||i(!1,null)},_)}export{m as i};

View file

@ -1,2 +0,0 @@
import"./Bzak7iHL.js";import{t as h,a as o,j as f,e as a,r as s,h as c}from"./wpu9U-D0.js";import{s as p}from"./D8mhvFt8.js";import{s as k}from"./LDOJP_6N.js";import{i as _}from"./DKve45Wd.js";import{s as I}from"./60_R_Vbt.js";import{p as P}from"./ByYB047u.js";import{I as H}from"./D7A-gG4Z.js";var q=f('<p class="text-sm text-dim mt-0.5 text-pretty svelte-162svzm"> </p>'),A=f('<div class="flex items-center gap-2 shrink-0 flex-wrap justify-end svelte-162svzm"><!></div>'),B=f('<header class="flex items-start justify-between gap-4 mb-6 enter svelte-162svzm"><div class="flex items-center gap-3.5 min-w-0 svelte-162svzm"><div><!></div> <div class="min-w-0 svelte-162svzm"><h1 class="text-2xl font-bold text-aurora leading-tight text-balance svelte-162svzm"> </h1> <!></div></div> <!></header>');function M(u,e){let v=P(e,"accent",3,"synapse");var l=B(),m=a(l),i=a(m),b=a(i);H(b,{get name(){return e.icon},size:22,draw:!0}),s(i);var x=c(i,2),d=a(x),g=a(d,!0);s(d);var z=c(d,2);{var w=t=>{var r=q(),n=a(r,!0);s(r),h(()=>p(n,e.subtitle)),o(t,r)};_(z,t=>{e.subtitle&&t(w)})}s(x),s(m);var y=c(m,2);{var j=t=>{var r=A(),n=a(r);k(n,()=>e.children),s(r),o(t,r)};_(y,t=>{e.children&&t(j)})}s(l),h(()=>{I(i,1,`header-tile relative flex items-center justify-center w-11 h-11 rounded-xl shrink-0
bg-${v()??""}/12 border border-${v()??""}/25 text-${v()??""}-glow`,"svelte-162svzm"),p(g,e.title)}),o(u,l)}export{M as P};

View file

@ -0,0 +1,2 @@
const e=[...`
\r\f \v\uFEFF`];function o(t,f,u){var n=t==null?"":""+t;if(f&&(n=n?n+" "+f:f),u){for(var s of Object.keys(u))if(u[s])n=n?n+" "+s:s;else if(n.length)for(var i=s.length,l=0;(l=n.indexOf(s,l))>=0;){var r=l+i;(l===0||e.includes(n[l-1]))&&(r===n.length||e.includes(n[r]))?n=(l===0?"":n.substring(0,l))+n.substring(r+1):l=r}}return n===""?null:n}function c(t,f){return t==null?null:String(t)}export{c as a,o as t};

View file

@ -1 +1 @@
import{a2 as g,a3 as d,o as c,P as m,a4 as i,a5 as b,g as p,a6 as v,a7 as h,a8 as k}from"./wpu9U-D0.js";function x(t=!1){const a=g,e=a.l.u;if(!e)return;let f=()=>v(a.s);if(t){let n=0,s={};const _=h(()=>{let l=!1;const r=a.s;for(const o in r)r[o]!==s[o]&&(s[o]=r[o],l=!0);return l&&n++,n});f=()=>p(_)}e.b.length&&d(()=>{u(a,f),i(e.b)}),c(()=>{const n=m(()=>e.m.map(b));return()=>{for(const s of n)typeof s=="function"&&s()}}),e.a.length&&c(()=>{u(a,f),i(e.a)})}function u(t,a){if(t.l.s)for(const e of t.l.s)p(e);a()}k();export{x as i};
import{az as g,aA as d,aB as c,v as m,aC as i,aD as b,g as p,aE as v,z as h,aF as k}from"./CpWkWWOo.js";function x(t=!1){const a=g,e=a.l.u;if(!e)return;let f=()=>v(a.s);if(t){let n=0,s={};const _=h(()=>{let l=!1;const r=a.s;for(const o in r)r[o]!==s[o]&&(s[o]=r[o],l=!0);return l&&n++,n});f=()=>p(_)}e.b.length&&d(()=>{u(a,f),i(e.b)}),c(()=>{const n=m(()=>e.m.map(b));return()=>{for(const s of n)typeof s=="function"&&s()}}),e.a.length&&c(()=>{u(a,f),i(e.a)})}function u(t,a){if(t.l.s)for(const e of t.l.s)p(e);a()}k();export{x as i};

View file

@ -1 +0,0 @@
var B=Object.defineProperty;var g=i=>{throw TypeError(i)};var F=(i,e,s)=>e in i?B(i,e,{enumerable:!0,configurable:!0,writable:!0,value:s}):i[e]=s;var w=(i,e,s)=>F(i,typeof e!="symbol"?e+"":e,s),M=(i,e,s)=>e.has(i)||g("Cannot "+s);var t=(i,e,s)=>(M(i,e,"read from private field"),s?s.call(i):e.get(i)),l=(i,e,s)=>e.has(i)?g("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(i):e.set(i,s),y=(i,e,s,a)=>(M(i,e,"write to private field"),a?a.call(i,s):e.set(i,s),s);import{w as D,Y as C,Z as k,_ as G,D as x,$ as A,G as S,M as Y,a0 as Z,a1 as $}from"./wpu9U-D0.js";var r,n,h,u,p,_,v;class z{constructor(e,s=!0){w(this,"anchor");l(this,r,new Map);l(this,n,new Map);l(this,h,new Map);l(this,u,new Set);l(this,p,!0);l(this,_,()=>{var e=D;if(t(this,r).has(e)){var s=t(this,r).get(e),a=t(this,n).get(s);if(a)C(a),t(this,u).delete(s);else{var c=t(this,h).get(s);c&&(t(this,n).set(s,c.effect),t(this,h).delete(s),c.fragment.lastChild.remove(),this.anchor.before(c.fragment),a=c.effect)}for(const[f,o]of t(this,r)){if(t(this,r).delete(f),f===e)break;const d=t(this,h).get(o);d&&(k(d.effect),t(this,h).delete(o))}for(const[f,o]of t(this,n)){if(f===s||t(this,u).has(f))continue;const d=()=>{if(Array.from(t(this,r).values()).includes(f)){var b=document.createDocumentFragment();Z(o,b),b.append(x()),t(this,h).set(f,{effect:o,fragment:b})}else k(o);t(this,u).delete(f),t(this,n).delete(f)};t(this,p)||!a?(t(this,u).add(f),G(o,d,!1)):d()}}});l(this,v,e=>{t(this,r).delete(e);const s=Array.from(t(this,r).values());for(const[a,c]of t(this,h))s.includes(a)||(k(c.effect),t(this,h).delete(a))});this.anchor=e,y(this,p,s)}ensure(e,s){var a=D,c=$();if(s&&!t(this,n).has(e)&&!t(this,h).has(e))if(c){var f=document.createDocumentFragment(),o=x();f.append(o),t(this,h).set(e,{effect:A(()=>s(o)),fragment:f})}else t(this,n).set(e,A(()=>s(this.anchor)));if(t(this,r).set(a,e),c){for(const[d,m]of t(this,n))d===e?a.unskip_effect(m):a.skip_effect(m);for(const[d,m]of t(this,h))d===e?a.unskip_effect(m.effect):a.skip_effect(m.effect);a.oncommit(t(this,_)),a.ondiscard(t(this,v))}else S&&(this.anchor=Y),t(this,_).call(this)}}r=new WeakMap,n=new WeakMap,h=new WeakMap,u=new WeakMap,p=new WeakMap,_=new WeakMap,v=new WeakMap;export{z as B};

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