
Memwright
io.github.bolnet由 GitHub 账号 bolnet 发布v0.1.3更新于 Oct 25, 2026
Embedded memory for AI agents with SQLite, pgvector, and Neo4j graph search.
概览
一个自托管的 AI 代理记忆层,可在 SQLite、pgvector 和 Neo4j 后端之间存储、检索并审计共享记忆。
- 可以做什么
- 添加、检索和取代记忆,附带来源信息、基于角色的访问控制和每个代理的 token 预算,并可查询某一历史时点的状态。运行保留策略、经审计的用户清除等治理任务,以及类 GDPR 的导出和删除审计回读。可选使用 Ed25519 对记忆签名并验证。可作为 Python 库、REST 边车或基于 stdio 的 MCP 服务器运行。
- 适用场景
- 当多个代理或会话需要共享一份可审计、带访问控制和历史的记忆,而不是单个助手的草稿本时,值得加入。也适用于需要证明某条记忆的来源或重建过去认知的场景。
- 运行要求
- 以本地 Python 包(pip install)或 Docker 镜像运行;快速启动需要 Docker 和本地 Ollama bge-m3 嵌入模型。后端为 Postgres/pgvector、向量存储和 Neo4j。可选密钥:NEO4J_PASSWORD、OPENAI_API_KEY 或 OPENROUTER_API_KEY(云端嵌入);pgvector 需 PG_CONNECTION_STRING。
安装
在 SourceWeft 中
- 打开 控制台中的 Memwright,将其添加到工作区。
- 为需要使用其工具的对话启用该服务。
Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。
其他 MCP 客户端
参照 仓库 中的启动说明。
README
Attestor
Governed memory for multi-agent production meshes.
Attestor is no longer "memory for your coding assistant." Three things happened: the editor vendors shipped memory for free, the benchmark race became a race about which LLM answers the question, and every serious conversation about a memory layer was about something else entirely: when many agents share one memory, who is allowed to write, who is allowed to forget, and can you prove it afterwards. So that is what Attestor is now.
When many agents share one memory, recall is the easy part. The hard part is who may write, who may forget, what an answer was built from, and whether you can prove all of it later. Attestor is a self-hosted memory service that makes those properties enforceable in code rather than in policy documents: role-based access at the AgentContext layer, provenance on every memory (optionally Ed25519-signed), per-agent token budgets and write quotas, a deterministic ranking path with no LLM in it, temporal supersession you can query as of any past moment, hard tenant isolation across all three storage roles, an auditable, audit-first forget-user path with declarative retention, and governance jobs that run as durable Temporal workflows: retried by policy, resumed after a crash, visible in an operator UI, and never on the read path. It runs as a Python library, a REST sidecar, or an MCP server with the same API. Role enforcement is an AgentContext guarantee and runs in process. The REST and MCP surfaces call the store directly and rely on their own auth.
[PyPI] [PyPI Downloads] [GitHub Stars] [Build] [Evals] [License: MIT]
Designed and built by Surendra Singh — building auditable infrastructure for multi-agent AI, with fifteen years of production-systems discipline brought to the memory layer. Companion projects:
claude-finance(Claude-powered financial analytics) ·private-equity(PE × AI workshop). Reach out if you're hiring senior IC for AI infrastructure.
What it is
Attestor is the memory layer for an agent mesh that has to answer to someone — a security review, a regulator, a customer, or the engineer paged at 3 a.m. Every memory is stored once in Postgres (the source of truth) and derived into Pinecone (vectors) and Neo4j (graph). Every read and write goes through an AgentContext that carries identity, role, namespace, and budget, and the store keeps enough history that any past state can be reconstructed.
What it enforces
Each guarantee below is a property of the code, not a roadmap item. File references point at the enforcing implementation.
How to evaluate it
The nearest neighbours are the temporal-knowledge-graph memory layers (Zep, Graphiti). For a production mesh the useful comparison is not benchmark accuracy — it is whether the memory layer itself enforces roles, budgets, tenancy, provenance, and auditable deletion, or leaves each of those to the calling application. The table above is Attestor's answer; hold any alternative to the same checklist, with file references.
Designed for
- Multi-agent products where many LLM-driven agents with different trust levels write to the same memory
- Regulated systems that need point-in-time reconstruction and a defensible deletion story
- Self-hosted deployments — your VPC, your Postgres, your Pinecone, your Neo4j
Not designed for
- A personal memory for a single coding assistant — the Claude Code plugin below is an on-ramp demo of the same engine, not the product
- A general-purpose vector database or a RAG framework with chunking / reranking / orchestration
- An LLM agent runtime — Attestor is the memory backend; the agent loop is yours
Try it locally in one command.
pipx install attestor && attestor quickstartbrings up Postgres + Pinecone Local + Neo4j in Docker and wires the Claude Code MCP server + hooks so you can watch the governance layer work on a real project. Details in Quick start and Install for Claude Code below.
Quick start
1. Install
Or pull the container (introspection-grade image, single layer over python:3.12-slim, currently linux/amd64):
Same image is mirrored to:
(An internal Azure ACR mirror exists at memwright.azurecr.io/attestor but is private — Azure customers should use az acr import from one of the public registries above.)
The image's default entrypoint is attestor mcp (MCP server over stdio). For full production use, point the container at an external Postgres + Neo4j via env vars (or compose them with attestor/infra/local/docker-compose.yml); override the entrypoint to run attestor doctor, attestor api, etc.
2. Stand up the local stack — one command, zero questions
attestor quickstart does the whole local install non-interactively and prints every step: it writes ~/.attestor/{config.toml,attestor.yaml,.env}, brings up the three-role local stack in Docker, uses a local Ollama bge-m3 embedder (no cloud key), wires the Claude Code MCP server (./.mcp.json) + lifecycle hooks, and runs attestor doctor.
Prerequisites: Docker running, and Ollama serving bge-m3 (ollama pull bge-m3). quickstart runs a preflight that scans the ports/tools and tells you if anything is missing — it never prompts.
To reverse it later: attestor teardown (zero-question; keeps your data volumes by default — --purge also wipes them, --dry-run previews).
In Claude Code, drive the same install conversationally: /plugin marketplace add bolnet/attestor → /plugin install attestor (then enable it), and run /attestor:install-attestor — it runs attestor quickstart for you. Cloud/managed backends (Neon / RDS / Cloud SQL, Pinecone Cloud, Neo4j AuraDB) and alternative embedders (Pinecone Inference llama-text-embed-v2, Voyage voyage-4, OpenAI text-embedding-3) are configured in ~/.attestor/attestor.yaml (the single source of truth) — see docs/INSTALL.md.
attestor doctor (run automatically at the end, or any time) checks all four subsystems: Document Store (Postgres), Vector Store (Pinecone), Graph Store (Neo4j), Retrieval Pipeline. The only hard dependency that cannot be down is the document store (Postgres); transient vector-probe failures are surfaced in the response trace rather than swallowed (retrieval/orchestrator/debug.py — vector_error field).
3. Use it
SOLO mode (zero-config). In v4,
AgentMemory().add('foo')auto-provisions a singletonlocaluser, an Inbox project (metadata.is_inbox=true), and a daily session — so the snippet above works on a fresh database without configuring identity (core.py:179-209). For multi-tenant production use, pass an explicitAgentContextwith a realnamespace.
4. Run a smoke benchmark (optional)
Verify your install end-to-end against a tiny LongMemEval slice. Defaults come from configs/attestor.yaml: Pinecone Inference llama-text-embed-v2 (1024-D) embedder + Pinecone vector store, openai/gpt-5.5 answerer, dual judges (openai/gpt-5.5 + anthropic/claude-sonnet-4-6), parallel=2.
Every model and parameter comes from YAML — see § Benchmarking below for the full bench harness.
Benchmarking
Every benchmark — smoke, single slice, full sweep, synthetic supersession — reads its knobs from two YAMLs:
The two files must have disjoint keys. The CI test tests/test_config_no_duplicate_keys.py enforces this; the bench loader (attestor.bench_config.get_bench) crashes on overlap. If you want a one-off override (different model for one bench run), use an env var or CLI flag — never duplicate the key in bench.yaml.
What LongMemEval is
LongMemEval (Wu et al., 2024 — published at ICLR '25) is the canonical benchmark for memory-augmented chat assistants. It measures whether an AI system can correctly answer questions that require recalling facts from long, multi-session conversation histories — the exact scenario Attestor is built for.
500 questions, 6 reasoning categories, 3 haystack sizes. Same questions across all three sizes; only the noise around the answer-bearing session changes:
LME-S is the headline number to beat. A memory layer that scores within 5% of a long-context baseline at 30× lower token cost is the marketing pitch.
The 6 reasoning categories (cleaned LME-S, 500 questions total — note: no abstention slice in the cleaned split, which the synthetic supersession suite covers):
Why this benchmark for Attestor: the temporal-reasoning and knowledge-update slices directly exercise features that distinguish Attestor from a vanilla RAG: bi-temporal recall, supersession-on-contradiction, event-time vs transaction-time disambiguation. A high score on those slices is the regulated-AI / audit / compliance pitch.
For the published Attestor numbers, see docs/bench/ — bench artifacts persist as lme-{variant}-{category}-{date}.{report,summary}.json. The Reporting section below shows how to render them as a table.
Download the LongMemEval dataset (one-time, before any bench run)
All lme_*.sh scripts use the cleaned LongMemEval split published on HuggingFace by xiaowu0162/longmemeval-cleaned. It auto-downloads on first use, but you'll want to know what's happening.
Cache location (created on first call):
(Or $XDG_CACHE_HOME/attestor/longmemeval/ if you set XDG_CACHE_HOME.)
Variants and on-disk sizes:
Option A — auto-download (recommended)
Just run any bench command. The first call downloads and caches; every subsequent call reads from disk:
You only pay the download cost once per variant. Internet flake during the first run? Delete the partial file in the cache dir and rerun.
Option B — pre-warm the cache (offline / CI)
Pre-fetch every variant you plan to use before the bench day:
Expected output:
Option C — manual download (firewalled environments)
If your runner can't reach huggingface.co, fetch the files on a connected machine and drop them into the cache dir manually:
The bench harness checks for these filenames exactly — don't rename them.
Verify the dataset is loadable
After download (auto or manual), confirm the loader picks it up cleanly:
Expected for the cleaned s variant (500 questions, 6 categories — note: no abstention slice in the cleaned split):
If counts don't match, the file is truncated — re-download.
Quick smoke (≤ 1 minute, ≤ $0.10)
Confirm the pipeline runs end-to-end before committing or running anything bigger:
oracle is the cheapest variant (gold sessions only, no distractor haystack). Schema is reapplied automatically; pass --skip-schema if you want to keep a populated DB between runs.
Single category — scripts/bench/lme_run.sh
Valid --category values: single-session-user, single-session-assistant, single-session-preference, multi-session, temporal-reasoning, knowledge-update. See What LongMemEval is above for sample counts and what each category tests.
Each run persists two files:
Full sweep — scripts/bench/lme_all.sh
Iterates bench.yaml's lme.categories list in order. Adding/removing slices is a YAML edit, not a script edit:
If one slice fails, the script logs it and moves on to the next.
Reporting — scripts/bench/lme_report.py
Aggregates every docs/bench/lme-*.summary.json into one markdown table; picks the most-recent file per (variant, category):
Default mode (latest-per-slice):
Trend mode (--trend) reads docs/bench/trend.csv — one row appended per bench run (auto-populated by lme_run.sh) — and shows progression with a Δ column:
The Features column records exactly which retrieval/answerer flags were enabled per run, so you can see at a glance which knob produced which lift.
Retrieval + answerer feature flags
Five orthogonal features land via configs/attestor.yaml boolean flips. All disabled by default — pick one per bench run, measure the lift, decide which to ship enabled.
multi_query and hyde are mutually exclusive in this release (multi_query wins if both flags are on with a logged warning). self_consistency and critique_revise are similarly mutually exclusive on the answerer side. Combinations across the two sides (e.g. hyde + self_consistency) are fine.
HyDE v2 prompt (attestor/retrieval/hyde.py) — generates an event-descriptive snippet rather than an answer-shape response, so the embedding lands close to source-shape conversation turns instead of question-shape queries. This is the lever that produced the +10pp measured lift on LME-S temporal-reasoning. temperature=0 is pinned so re-runs are deterministic.
Honest negative results documented above — multi_query and temporal_prefilter did NOT generalize from their literature numbers on the LME-S temporal-reasoning slice. multi_query paraphrases stay in question-shape and RRF dilutes marginal hits; temporal_prefilter heuristic anchors don't help interrogative-form questions ("how many days ago…"). HyDE was the right tool. Per-feature methodology + diagnostic artifacts in docs/bench/pinecone-lme-temporal-diagnostic-{baseline,mq3,hyde,hyde-bm25}-20260429.json.
Cross-vector-DB diagnostic harness — experiments/pinecone_lme_temporal_diagnostic.py runs retrieval-only LME-S diagnostics against Pinecone Local with --baseline / --multi-query / --hyde / --bm25-hybrid / --temporal-prefilter / --category flags. No answerer, no judge — pure recall@K ceiling. --skip-ingest reuses populated namespaces for fast retrieval-flag iteration (~60s for 30q vs ~50min with fresh ingest).
To benchmark a single feature: flip its enabled: true in configs/attestor.yaml, run the bench slice, compare against a same-day baseline run with everything off. The trend table will show the delta in the Δ column.
Synthetic supersession suite — python -m evals.knowledge_updates
50 hand-curated cases, 10 contradiction categories × 5 each (numeric, categorical, temporal, preference, entity, locational, intent, relational, count, status_binary). Each case ingests two sessions (Session 1 states a fact, Session 5 contradicts it) and asks a question that should resolve to the newer fact. Metric: % of cases where retrieval surfaces the new fact as top-1.
Outputs:
Target score (configurable in bench.yaml): 92% new_wins. Below that, the supersession-confidence-decay weight in attestor/retrieval/scorer.py needs tuning.
Cost & runtime guide
Approximate, at reasoning_effort=high for answerer + judge, parallel=2, OpenRouter pricing:
To cut costs, edit configs/attestor.yaml's models.reasoning_effort.{answerer,judge} from high → medium or low.
Configuration cheat sheet — configs/bench.yaml
Architecture
Bi-temporal — replay any past state
Every memory carries two time axes:
Plus a superseded_by chain. Old facts are never deleted — they remain queryable forever (attestor/temporal/manager.py:30-66).
as_of and time_window propagate end-to-end through the orchestrator and document store. Auto-supersession on write is wired into core.py:add() (core.py:762, 784-785): on every add, the temporal manager finds active rows with the same (entity, category, namespace) and different content, marks them superseded, sets valid_until=now, and links superseded_by=<new_id>. Detection is rule-based string equality today.
Tenant isolation — Postgres Row-Level Security
Every tenant table (users, projects, sessions, episodes, memories, user_quotas, deletion_audit) carries a tenant_isolation_* policy keyed off the attestor.current_user_id session variable. An empty / unset value fails closed — no rows visible (attestor/store/schema.sql:311-327).
Honest disclosure. Enforcement lives in Postgres, not Python. The
AgentRoleenum inattestor/context.py:49-56is metadata that flows onto memories for provenance; it does not gate operations in Python. RLS is what actually controls access. This is correct architecture for a memory backend, but worth knowing if you read the Python alone.
The retrieval pipeline — semantic-first, six steps
attestor/retrieval/orchestrator/core.py runs the same six steps for every query:
- Vector top-K — Pinecone cosine, k=50 (pgvector remains as opt-in fallback for self-contained deploys)
- Graph narrow — Neo4j BFS depth ≤ 2 from each candidate's entity to the question entities; affinity bonus per hop (0-hop=+0.30, 1-hop=+0.20, 2-hop=+0.10; unreachable=−0.05). Discrete, not "soft".
- Triples inject — typed-edge facts (
uses,authored-by,supersedes) injected as synthetic memories - MMR rerank — λ=0.7
- Confidence decay + temporal boost — recency lifts; stale, low-confidence rows fall
- Budget fit — greedy monotonic-by-score pack into the caller's token budget
Every call writes a JSONL trace to logs/attestor_trace.jsonl (disable via ATTESTOR_TRACE=0).
Async retrieval — lower latency without weakening audit
Independent recall steps run concurrently via asyncio.gather, but none of the eight audit invariants are relaxed. You don't trade trust for speed — you get both.
Write-side stays sync. All add(), update(), supersession writes are explicitly non-goals for the async refactor — the audit chain depends on serial write ordering and the bi-temporal t_created order must be linearizable per row. Async is read-side only.
Trace stays reconstructable. Every event carries recall_id + monotonic seq + optional parent_event_id, so the audit dashboard renders concurrent recalls as a tree of events rather than a stream — (recall_id, seq) reconstructs causal order from the JSONL log.
Same recall(as_of=X) replay guarantee. A past recall remains byte-for-byte reproducible from the bi-temporal columns + deletion_audit + the trace JSONL — async parallelism doesn't change what gets read, only when. The load-bearing test (tests/test_as_of_replay.py) is in the regression gate of every async PR.
Full design + audit-invariant matrix: docs/plans/async-retrieval/PLAN.md. Convention: every async PR ships with an audit-preservation argument and the matching invariant test (tests/async_retrieval/test_audit_invariants_under_async.py) GREEN before merge.
Three storage roles
Postgres is the source of truth. Pinecone vectors and Neo4j graph are derived state, both rebuildable from Postgres — but both are required for the canonical install: vector cosine is step 1 of the retrieval pipeline, graph expansion is step 2, and conversation ingest writes typed edges. The only role that cannot be down is the document store; the orchestrator records transient vector-probe failures in the response trace (vector_error) instead of swallowing them.
Durable governance jobs — the read path never waits, the jobs never quit
Governance work that used to be a hand-rolled queue, a docstring promising a sweeper, or a warning-only write runs as Temporal workflows (attestor/durable/). Opt-in: durable.enabled: false by default; with it off, or the server unreachable, every job falls back to the in-process path it replaced.
Two hard rules, enforced by tests/test_durable_isolation.py: recall never touches Temporal and hooks never wait on it (nothing under attestor/retrieval/** or attestor/hooks/** may import attestor.durable or temporalio), and workflow payloads carry ids, never memory content, so Postgres stays the only source of truth and Temporal holds job state only. Driver exceptions are sanitized before they reach workflow history.
Optional BM25 / FTS lane
A trigger-maintained content_tsv tsvector + GIN index lifts queries that embeddings under-recall (acronyms, IDs, rare proper nouns). Enabled when v4 schema is detected; fuses with the vector lane via Reciprocal Rank Fusion (RRF, k=60). Graceful no-op on backends without the column (core.py:122-130).
Conversation ingest
The heavyweight write path that turns conversation turns into auditable memories. core.py:ingest_round(turn) orchestrates four passes:
Two-pass speaker-locked extraction
attestor/extraction/round_extractor.py:216, 258 — separate prompts for user vs assistant turns. The user-turn extractor only emits facts attributable to the user; the assistant-turn extractor only emits facts the assistant introduced. Stops cross-attribution. The "+53.6 over Mem0" delta in our LongMemEval scores comes from this split.
Four-decision conflict resolver
attestor/extraction/conflict_resolver.py:40, 98 — for each newly-extracted fact, an LLM call against existing similar memories returns one of:
Each Decision carries evidence_episode_id. Every supersession is auditable. Failsafe: parse failure on a single fact yields ADD-by-default — better a duplicate-ish row than a silent drop.
Two write paths, two contracts.
mem.add(...)runs the lightweight rule-based supersession (§Bi-temporal).mem.ingest_round(turn)runs the full four-decision pipeline. Pickingest_roundfor conversational data; pickaddfor structured writes where you've already done the conflict reasoning.
Sleep-time consolidation
mem.consolidate() (core.py:526) re-extracts and synthesizes facts from recent episodes with a stronger model. Currently a Python-API-only call — no CLI command. Schedule it from your application (cron, systemd timer, ECS scheduled task) when you want fresher facts than the streaming extractor produces.
Reflection engine
attestor/consolidation/reflection.py runs periodic synthesis across N episodes for one user. Outputs:
stable_preferences— patterns appearing in 3+ episodesstable_constraints— rules the user repeatedly invokeschanged_beliefs— preferences that shifted (old → new, with explicit invalidate)contradictions_for_review— flagged for HUMAN REVIEW, not auto-resolved
The "do not auto-resolve" stance is the load-bearing piece for regulated chat systems. The prompt is explicit (reflection.py:35-66): "Do NOT auto-resolve contradictions. Flag them for human review."
Chain-of-Note reading
recall() returns a list. recall_as_pack() returns a typed retrieval envelope an agent can actually reason about — every field a Chain-of-Note flow needs to cite, abstain, or pick the right validity window when memories conflict:
ContextPack is frozen=True, hashable, JSON-serializable. It drops cleanly into a tool call. The default prompt has explicit ABSTAIN and CONFLICT clauses — every frontier model defaults to confabulation otherwise.
Multi-agent primitives
Six roles
AgentRole: ORCHESTRATOR, PLANNER, EXECUTOR, RESEARCHER, REVIEWER, MONITOR (attestor/context.py:49-56). The role flows onto every memory's metadata for provenance. Access enforcement is two-layer:
- AgentContext layer —
ROLE_PERMISSIONSmatrix gates writes / forgets per role. Matrix:ORCHESTRATOR = R+W+F;PLANNER/EXECUTOR/RESEARCHER=R+W;REVIEWER/MONITOR=Ronly.read_only=Trueis an independent kill switch. - Postgres RLS layer — row-level filter on
user_id(see §Tenant isolation).
AgentContext — handoff, scratchpad, trail
as_agent() creates a child context with parent_agent_id, full agent_trail, and a shared scratchpad. The trail accumulates — useful for proving "this answer came from agent X who got it from agent Y."
Per-agent token budgets
AgentContext.token_budget (default 20 000) is enforced — recall() packs results greedily until the budget is exhausted (scorer.py:fit_to_budget). token_budget_used accumulates across calls in a session.
Optional write quotas
mem.set_quota(user_id, daily_writes=...) → enforced on add against the v4 user_quotas table (core.py:592-621). Optional; unset means unlimited.
Security & Compliance
Row-Level Security
Cross-link to §Tenant isolation. RLS policies are the access-control surface; the Python layer trusts them. Set attestor.current_user_id per connection.
Provenance on every memory
Every memory carries agent_id, session_id, source_episode_id. The supersession chain (superseded_by) is preserved forever. Conversation episodes are stored verbatim, separate from the memories extracted from them — meaning you can always reconstruct which conversation turn produced which fact.
Deletion audit log
Hard deletes (e.g., GDPR purges) write a row to deletion_audit before the cascade — what was deleted, when, why, by whom. This is the carve-out for the otherwise-immutable schema.
GDPR — export and purge
core.py:557-590. v4 only. Returns / writes everything Subject Access requires for Art. 15 / Art. 17.
Optional: Ed25519 provenance signing
Enable via config (signing.enabled = true). On every add, attestor signs the canonical payload id || agent_id || t_created || content_hash with an Ed25519 key. mem.verify_memory(memory_id) returns bool (core.py:623-640). Optional, off by default — turn on for adversarial-write contexts where you need cryptographic non-repudiation.
Runtime topologies
Same API across all three. Only configuration changes.
Backends
Override the default via config:
Reference Terraform lives under attestor/infra/.
Embeddings
Provider auto-detect (attestor/store/embeddings.py:get_embedding_provider), in this order:
- Local Ollama
bge-m3— 1024-D, 8K context — used whenhttp://localhost:11434is reachable - Cloud-native — Bedrock Titan / Vertex / Azure OpenAI when their SDK + creds are present
- OpenAI
text-embedding-3-large(3072-D native; pinOPENAI_EMBEDDING_DIMENSIONS=1024for schema compat) - OpenRouter — for federated runs
Local-first by design. Override:
CLI
attestor --help lists everything. The most useful commands:
MCP server
attestor mcp (or attestor serve <path>) exposes an MCP stdio server with eight tools:
Plus MCP resources (memory listings) and prompts (canned recall prompts for IDE assistants).
Hooks (Claude Code)
Three lifecycle hooks ship in attestor/hooks/:
session_start— injects relevant memories into the session context based on cwd / repopost_tool_use— auto-captures useful artifacts fromWrite/Edit/Bashstop— writes a session summary on exit
Wire them up via the installer (next section) or by hand in ~/.claude/settings.json.
Install for Claude Code
The one command. pipx install attestor then attestor quickstart — zero questions, one default profile. It brings up the local backends, uses a local Ollama bge-m3 embedder (no cloud key), wires the MCP server (./.mcp.json) + lifecycle hooks, runs attestor doctor, and prints every step. Reverse it any time with attestor teardown.
Prerequisites: Docker running + Ollama serving bge-m3 (ollama pull bge-m3). quickstart's preflight scans for these and reports what's missing — it never prompts.
Driving it from inside Claude Code (plugin). Install the plugin once, then run the command it provides:
Plugin commands are namespaced: the command is
/attestor:install-attestor(and/attestor:uninstall-attestor), not a bare/install-attestor. A freshly-installed plugin can be disabled — enable it in the/plugin→ Installed menu and/reload-plugins, or the command won't resolve.
Memory is isolated per project automatically — each working directory (git root, else cwd) is its own hard-isolated tenant, so projects never share memory. No namespace to configure.
The local backends come up as three Docker containers (the bundled attestor/infra/local/docker-compose.yml, which quickstart runs):
Every container, volume, and the compose network/project is named
attestor_…, sodocker ps -a \| grep attestor(anddocker volume ls \| grep attestor) lists everything Attestor owns.
Cloud / managed backends (Neon · RDS · Cloud SQL, Pinecone Cloud, Neo4j AuraDB) and alternative embedders (Pinecone Inference llama-text-embed-v2, Voyage voyage-4, OpenAI text-embedding-3) are configured in ~/.attestor/attestor.yaml — see docs/INSTALL.md.
Install as a Skill (2026 agent SDKs)
Attestor ships with a canonical SKILL.md at skills/attestor-memory/SKILL.md. Both Anthropic (skills-2025-10-02) and OpenAI's Responses API converged on this format — a markdown file with YAML frontmatter — for distributing reusable agent expertise. The wheel ships the SKILL.md, so every 2026-grade harness can auto-discover it after a single pip install attestor.
The skill teaches the agent the six core primitives (recall, add, timeline, current_facts, forget, audit) plus the v4 enterprise surface (bi-temporal as_of replay, RBAC roles, namespace isolation, provenance signing, GDPR export / purge). Every code example references methods that actually exist on attestor.AgentMemory, and a CI test (tests/test_skill_md.py) keeps the SKILL.md from drifting from the live API.
To pin the contract in your own host:
Evaluation
Boundary statement. The dual-LLM judge stack is a benchmarking mechanism, not the runtime contract. Recall in production is single-pipeline and deterministic. Multiple judges score answers in evaluation only — never in user-facing reads.
The smoke driver mirrors the canonical published-benchmark stack exactly. See --help for the full env-var / CLI-flag override matrix.
Project layout
Development
Style: black formatting, isort imports, ruff lint, mypy types. PEP 8, type-annotated signatures, dataclasses for DTOs. Many small files (200–400 lines typical, 800 max).
Conventions worth knowing:
- Postgres is the source of truth. Neo4j is derived; rebuild it from Postgres if it drifts.
- Non-fatal errors in vector / graph paths are caught and logged. The document path never silently breaks.
- Configuration layering: env vars →
~/.attestor.toml→ in-code overrides. - Two write paths:
add()for structured (lightweight rule-based supersession),ingest_round()for conversational (full 2-pass + 4-decision contract).
Health check
Always call this first when integrating:
It probes Document Store (Postgres), Vector Store (pgvector), Graph Store (Neo4j), and the retrieval pipeline. All four are required for the default topology — graph expansion is step 2 of the canonical pipeline, not an optional accelerator. Transient vector-probe failures surface in the recall() trace (vector_error) so callers can distinguish a degraded result from a clean one.
Status & versioning
- Version: 4.1.6 (stable) — published to PyPI and the MCP Registry as
io.github.bolnet/attestor.pip install attestorreturns the latest 4.1.x (no--preflag needed). - v3 → v4: greenfield rebuild on a v4-native Postgres schema with hard tenant isolation, bi-temporal facts, and a no-LLM retrieval critical path. There is no automated migration. v3 was alpha-only with no production users; drop your v3 DB and reinstall.
- See
CHANGELOG.mdfor the full track-by-track changelog.
License
MIT. See LICENSE.
来源:README.md,提交 e6b5c18
工具
0版本历史
1- v0.1.3最新Sep 16, 2026
评分
已满足适用检查项的 50%
该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。
- 近 30 天内可连通25 分
请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。
- 已声明认证方式10 分
请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。
- 已列出工具15 分
请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。
- 描述必需15 分
- 图标10 分
- 文档15 分
- 许可证10 分
