cd /news/ai-agents/show-hn-tensemem-deterministic-memor… · home › topics › ai-agents › article
[ARTICLE · art-155120] src=github.com ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

Show HN: TenseMem – deterministic memory for AI agents

TenseMem, a new open-source memory layer for AI agents, was released as a pip-installable package that stores agent memory in a single SQLite file and keeps one current value per slot, with the engine rather than the model deciding which typed facts apply. In the project's packaged benchmark of 101 conversations run with its rule-based FakeExtractor and no network, TenseMem scored 1.00 on current-state accuracy, 0.00 on stale-fact leak, 1.00 on freshness honesty and 0.00 on contamination, versus 0.37 current-state accuracy and 1.00 stale-fact leak for both concatenated transcripts and token-overlap retrieval. The package ships adapters for OpenAI, Anthropic, Ollama, LiteLLM and Gemini, exposes Python, CLI, REST and MCP surfaces over a shared Memory/AsyncMemory core, and fences recalled context so the engine refuses to learn from its own output.

read5 min views6 publishedOct 22, 2026
Show HN: TenseMem – deterministic memory for AI agents
Image: Michielbdejong (auto-discovered)

Memory that knows its tenses. Embedded, deterministic memory that AI agents can safely act on.

One SQLite file. No server required. The model proposes typed facts; the engine decides what is current.

pip install tensemem
python
from tensemem import FakeExtractor, Memory

mem = Memory(llm=FakeExtractor())
mem.register_predicate("lives_in")
added = mem.add("u1", "I live in Delhi")
ctx = mem.context(user_id="u1", min_seq=added.seq, wait=1.5)
print(ctx.text)  # fenced block the model can read
print(ctx.freshness.is_current)  # True once extract finished

FakeExtractor is rule-based and needs no API key. Same API with tensemem[openai], tensemem[anthropic], tensemem[ollama], tensemem[litellm], or Gemini ( GEMINI_API_KEY in the process environment).

Similarity search returns both “I live in Delhi” and “I moved to Bangalore” and leaves the model to guess. TenseMem keeps one current value per slot, says whether the read is fresh, and refuses to learn from its own output.

  • One current truth per ONE slot. A new city supersedes the old one. History still answers “where did I live before?”
  • Every read reports freshness.as_of_seq ,pending ,is_current . Wait for a message, or see that it is not applied yet.
  • No self-echo. Recalledcontext() is fenced. Extract needs a verbatim quote from the user.
  • Corrections stick.correct() restores the displaced value and survivesrebuild() with a newer model.
  • The engine decides. The LLM emits typed ops. Deterministic rules apply them.
  • No infrastructure. Default store is one SQLite file inside your process.
mem.add("u1", "I live in Delhi")
mem.set(user_id="u1", predicate="lives_in", value="Bangalore")

print(mem.state("u1").values)

history = mem.history("u1", predicate="lives_in")

bangalore = next(f for f in history.facts if f.object == "Bangalore")
mem.correct(bangalore.id)  # that move was never true
print(mem.state("u1").values)
php
flowchart LR
  E[episodes] --> X[extractor proposes ops]
  X --> R[resolver]
  R --> F[facts]
  F --> C["context() + freshness"]
  C -.->|fence stripped| X

Every surface is an adapter over Memory / AsyncMemory. The model proposes typed ops; resolve() decides mutations; apply() writes one SQLite file. Facts are a rebuildable projection of an append-only episode log.

Editable source: docs/architecture.excalidraw. File map: Architecture.

If you are changing… Start here
Python / CLI / REST / MCP API src/tensemem/memory.py ,cli/main.py ,server.py ,mcp.py
Studio UI studio/app/ (board, memories, playground, echo, ops) andstudio/lib/api.ts
Extract prompt or worker pipeline/prompts/extract_v1.md ,pipeline/worker.py ,pipeline/extractor.py
What an op is allowed to do core/resolver.py (pure),pipeline/normalize.py ,pipeline/policy.py ,pipeline/guard.py
A new LLM or embedder providers/llm/ ,providers/embed/ — keys in process env only
SQLite schema or reads storage/sqlite/ (one writer; ADR 0003 / 0020)
Freshness, fence, corrections storage/sqlite/snapshot.py ,security/fence.py ,correct()
Invariants tests/core/ ,tests/property/ ,tests/golden/ — default pytest never calls a hosted model

Do not auto-register unknown predicates, skip keep_ops, or teach the engine from fenced context() text. Those are locked by tests.

tensemem bench scores the packaged 101 conversations with FakeExtractor (no network):

System Current-state accuracy Stale-fact leak Freshness honesty Contamination
TenseMem 1.00 0.00 1.00 0.00
Concatenated transcript 0.37 1.00 0.00 n/a
Token-overlap retrieval 0.37 1.00 0.00 n/a

Reproduce with tensemem bench. Methodology: TenseBench.

tensemem inspect --user u1
tensemem history --user u1 lives_in
tensemem search --user u1 "where did I live"

TenseMem Studio is a local Next.js inspector over tensemem serve (board, memories table, playground, compare, echo). It is not a chatbot. Secrets stay in the serve process environment — never in the Token field. Studio lives in the git checkout, not on PyPI.

git clone https://github.com/tensemem/tensemem.git
cd tensemem
pip install "tensemem[server]"

Terminal 1 — HTTP:

tensemem --db ./studio.db serve --port 8080

Terminal 2 — Studio:

cd studio && npm install && npm run dev
Extra What you get
tensemem[openai] /[anthropic] /[ollama] /[litellm] Hosted or local extract
tensemem[server] tensemem serve REST + Studio backend
tensemem[mcp] stdio or tensemem mcp --http
tensemem[langgraph] TenseMemStore (put /search )
tensemem[llamaindex] TenseMemChatMemory (put /get )
tensemem[local] FastEmbed vectors for search

Gemini extract uses GEMINI_API_KEY (or GOOGLE_API_KEY) in the environment of the process that runs extract. No extra package beyond the server if you use the built-in Gemini adapter.

--token / TENSEMEM_TOKEN is optional bearer auth on HTTP. Bind stays loopback unless you put a TLS proxy in front.

0.1.0 alpha. The Python API, CLI, REST, MCP, and Studio are usable on a single SQLite file. The default store is SQLite; a second SQL dialect is not shipped. Multi-tenant hosted Studio is not in this tree.

Quickstart · Concepts · Architecture · API · CLI · REST · Studio · MCP · Operations · ADRs

uvx mkdocs serve previews the same pages locally.

Default tests need no API keys and never call a model. From a git checkout:

git clone https://github.com/tensemem/tensemem.git
cd tensemem
uv sync
uv run ruff check .
uv run ruff format --check .
uv run pyright
uv run pytest

Studio (optional, when you change studio/):

cd studio && npm ci && npm run typecheck && npm run build

Live extract against a real model (skipped unless both are set):

uv sync --extra openai
TENSEMEM_LIVE=1 OPENAI_API_KEY=… uv run pytest -m live

Model keys belong in the process that runs extract (your script, or the tensemem serve terminal). They are never valid values for Studio Token.

Variable Used for
GEMINI_API_KEY orGOOGLE_API_KEY Gemini extract
OPENAI_API_KEY OpenAI extract / embeddings ( tensemem[openai] )
ANTHROPIC_API_KEY Anthropic extract ( tensemem[anthropic] )
TENSEMEM_LLM Default extractor name for tensemem serve (fake ,gemini ,openai , …)
TENSEMEM_LITELLM_MODEL Override Gemini / LiteLLM model id
TENSEMEM_TOKEN Optional bearer for tensemem serve /tensemem mcp --http (not a model key)
TENSEMEM_URL Studio rewrite target (default http://127.0.0.1:8080 )
TENSEMEM_LIVE Set to 1 withOPENAI_API_KEY to runpytest -m live

Ollama needs a local Ollama process, not an env key. Details: Studio, REST, SECURITY.md.

See CONTRIBUTING.md. main is protected; every change is a pull request. Merges wait until CI, PR quality, and CodeQL are green.

── more in #ai-agents 4 stories · sorted by recency
── more on @tensemem 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/show-hn-tensemem-det…] indexed:0 read:5min 2026-10-22 · —