Skip to main content

🫥 forgetted

Your AI agent remembers everything. Now it doesn't have to.

PyPI Downloads Stars License Python CI


forgetted is a small Python library that gives AI agents selective memory governance: inside a context-managed window the agent keeps full read access but its writes to memory files, session logs, deliverables, and (optionally) a vector store silently vanish and are cleaned up on exit. One with block, and your agent keeps full context but persists nothing.

Traditional incognito is all-or-nothing: no past, no future, fully isolated. forgetted keeps full read continuity while making the write side non-persistent for the duration of a window.

Part of the Hermes Labs reliability stack.

from forgetted import ForgetSession

with ForgetSession("/path/to/workspace"):
    agent.chat("this conversation never happened")
# ↑ Writes through `builtins.open` to protected workspace paths do not persist.
#    Add an adapter for mem0 or another persistence layer; reads still work.

Why?

AI agents write everything: memory files, session logs, vector embeddings, deliverables. Sometimes you need context without consequences:

  • 💬 Sensitive conversations that shouldn't persist in agent memory
  • 🧪 Experiments you don't want polluting your agent's knowledge base
  • 🔒 Client data discussed but not stored
  • 🤔 Brainstorming that shouldn't bias future responses

forgetted is not a prompt. It's software that wraps the agent's persistence layer — writes silently vanish, reads still work, and the agent resumes normally after.

Install

pip install forgetted

Quick Start

Simple (file-level protection)

import tempfile
from pathlib import Path

from forgetted import ForgetSession

workspace = Path(tempfile.mkdtemp())
(workspace / "memory").mkdir()
notes = workspace / "memory" / "notes.md"

# Inside the window, writes through `open()` to protected paths
# (memory/, DELIVERABLES.md, *.jsonl) silently vanish. Reads are never blocked.
with ForgetSession(str(workspace)):
    with open(notes, "w") as f:
        f.write("this conversation never happened")

print(notes.exists())  # False — the write was intercepted

# After the window, writes persist normally again.
with open(notes, "w") as f:
    f.write("this one is remembered")

print(notes.read_text())  # this one is remembered

With vector DB protection

from forgetted import ForgetSession
from forgetted.adapters.mem0 import Mem0Adapter

session = ForgetSession(
    workspace="/path/to/workspace",
    adapters=[Mem0Adapter(memory_instance, user_id="roli")],
)
session.start(checkpoint_summary="Discussing API design")
# ... conversation happens with full context, zero persistence ...
session.stop()  # re-enables all layers, cleans up

With framework-native read-only switches

HindsightAdapter and CrewAIAdapter don't patch anything — they flip the framework's own switch for the duration of the window and restore the exact prior value on exit (a memory already set read-only stays that way).

from forgetted import ForgetSession
from forgetted.adapters import CrewAIAdapter, HindsightAdapter

session = ForgetSession(
    workspace="/path/to/workspace",
    adapters=[
        HindsightAdapter(hindsight_client),
        CrewAIAdapter(crew_memory),
    ],
)
session.start()
# ... conversation happens with full context, zero persistence ...
session.stop()  # restores each layer's prior setting

HindsightAdapter requires a Hindsight client version that exposes retain_suspended; CrewAIAdapter requires a CrewAI memory that exposes read_only. Each raises AttributeError at construction if the attribute is missing, so an unsupported version fails loudly instead of silently not blocking.

Version status (verified 2026-09-11): crewai 1.15.21 on PyPI exposes Memory.read_only, so CrewAIAdapter blocks remember()/remember_many() with a released CrewAI; that release still writes access times on recall() and allows update() under read_only (fixed upstream in the still-open crewAIInc/crewAI#7367). No released hindsight-client (latest 0.9.2) exposes retain_suspended yet — it is added by vectorize-io/hindsight#4285, which is still open, so HindsightAdapter raises AttributeError against every published client until that lands.

Trigger detection (for chat agents)

from forgetted import is_forget_trigger, ForgetSession

if is_forget_trigger(user_message):  # "/forget", "off the record", etc.
    with ForgetSession(workspace):
        handle_conversation()

What Gets Blocked

Layer How Status
Memory files (memory/*.md) builtins.open patch ✅ Blocked
Deliverables / audit logs builtins.open patch ✅ Blocked
Session logs (*.jsonl) Blocked + deleted on exit ✅ Blocked
mem0 / semantic memory Method patch on add/update ✅ Blocked
Hindsight Framework-native retain_suspended flag ⏳ Pending upstream — needs a hindsight-client release containing vectorize-io/hindsight#4285
CrewAI memory Framework-native read_only flag ✅ Blocked
Any custom persistence Write your own adapter 🔌 Extensible

How It Works

forgetted uses a layered defense:

  1. FileWriteAdapter (always on) — patches builtins.open to intercept writes to protected paths. Returns no-op file handles instead of raising — agent code doesn't crash, writes just vanish.

  2. Mem0Adapter (opt-in) — patches memory.add() and memory.update() during the window. Post-window cleanup deletes any memories that leaked through.

  3. HindsightAdapter / CrewAIAdapter (opt-in) — flip the framework's own read-only switch (retain_suspended / read_only) for the window and restore the exact prior value on exit. No patching, no cleanup sweep needed.

  4. ForgetSession orchestrates everything: checkpoint → disable adapters → run conversation → enable adapters → cleanup → delete session log.

Reads are never blocked. The agent has full context — it just can't write new context.

Write Your Own Adapter

Any persistence layer can be controlled:

from forgetted.adapters.base import PersistenceAdapter

class RedisAdapter(PersistenceAdapter):
    name = "redis"

    def disable(self):
        self._client.config_set("save", "")
        self._active = True

    def enable(self):
        self._client.config_set("save", "3600 1")
        self._active = False

    def cleanup(self):
        for key in self._window_keys:
            self._client.delete(key)

    @property
    def is_active(self): return self._active

Register it: ForgetSession(workspace, adapters=[RedisAdapter(client)])

Trigger Phrases

Built-in detection for natural-language triggers:

Trigger Example
/forgetted "/forgetted"
/forget "/forget"
forget this "hey, forget this conversation"
off the record "let's go off the record"
forgetted mode "enable forgetted mode"
don't remember this "don't remember this"

Architecture

┌─────────────────────────────────────────┐
│           ForgetSession                  │
│  (orchestrator — context manager)        │
├─────────────────────────────────────────┤
│  ┌──────────────┐  ┌──────────────┐     │
│  │ FileWrite    │  │ Mem0         │     │
│  │ Adapter      │  │ Adapter      │ ... │
│  │ (safety net) │  │ (opt-in)     │     │
│  └──────────────┘  └──────────────┘     │
├─────────────────────────────────────────┤
│  checkpoint → disable → conversation    │
│  → enable → cleanup → delete log        │
└─────────────────────────────────────────┘

What This Really Is

This is not a UX toggle. It's a memory governance primitive.

Like git: you branch, but you never merge back. The conversation exists in context, and the writes it would normally make to the agent's persistent state are intercepted instead. After the window closes, a normal read of the agent's memory and logs shows no trace of it — within the scope described in Limitations.

"I want context… but I don't want consequences."

Tested

116 tests (113 pass, 3 xfail) including an adversarial suite:

  • ✅ Write blocking via builtins.open (modes w/a/x/wb/r+, symlinks resolved, binary)
  • ✅ Trigger detection (no false positive on "I'm so forgetful today", etc.)
  • ✅ Adapter error isolation (one failing adapter doesn't break others)
  • ✅ Exception safety (cleanup runs even if the conversation crashes)
  • ✅ Idempotency (double-start, stop-before-start, double-stop all safe)

Known bypasses (writes that do not go through builtins.open, such as Path.write_text/write_bytes and os.open) are documented as xfail tests rather than hidden. See Limitations below.

Threat Model

What forgetted blocks: Writes that go through builtins.open to protected workspace paths (memory/, DELIVERABLES.md, *.jsonl), plus mem0 add/update when the Mem0Adapter is registered.

What forgetted does NOT block: LLM API provider logs, network telemetry, OS-level forensics, and writes that bypass builtins.open (see Limitations). It is a convenience layer for agent-controlled persistence, not a security or containment boundary.

What you can rely on, within that scope: writes routed through builtins.open to protected paths are intercepted and return no-op handles, so a normal read of the agent's memory and logs afterward does not surface what happened inside the window. This is enforcement by effect (no-op handles + post-window cleanup), not a prompt instruction.

Limitations

forgetted is a software convenience layer, not a security boundary. Grounded in the code and the xfail test suite, it does not:

  • Catch writes that bypass builtins.open. Path.write_text, Path.write_bytes, os.open, C-extension writes, and subprocesses write directly and are not intercepted. These are documented as xfail tests.
  • Erase data written before the window opened. It governs writes during the window only; pre-existing memory is untouched.
  • Block reads. By design — the agent keeps full read context.
  • Intercept writes outside the declared workspace path.
  • Block network calls, API calls, or external tool use.
  • Support overlapping forgetted sessions stopped out of order. Properly nested (LIFO) sessions work, but stopping an outer session before an inner one restores the real open underneath the still-active inner window (xfail test).
  • Defend against an adversary inspecting LLM-provider logs, network traffic, or raw disk forensics.

For the full machine-readable behavior contract, see INTENT.md.

If forgetted is useful to you, please star the repo — it helps others find it.

License

Apache-2.0 — Hermes Labs


About Hermes Labs

Hermes Labs is an AI reliability engineering studio for product and engineering teams shipping production agents and LLM applications. We find the structural AI failures standard evals miss, then harden retrieval, memory, agents, and the language layers around production AI systems with runtime controls and defensible evidence.

Browse the open-source catalog or contact roli@hermes-labs.ai.

Metadata

Release files for forgetted 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for forgetted 0.3.0
File Size Uploaded
forgetted-0.3.0.tar.gz 32.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for forgetted 0.3.0
File Interpreter ABI Platform
forgetted-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 55.6 kB

Release files / forgetted-0.3.0.tar.gz

Download URL forgetted-0.3.0.tar.gz
Size 32.3 kB
Tags Source
SHA-256 checksum
How to use checksums
017b4fe27d6753782a1d57eaeaec0feec26e35f9c16666e58ad977d5c96d5061
BLAKE2b-256 checksum
How to use checksums
a61ff685bd3be075bbcc894c100385f288fa1bbbf7eee84776d136100e380ecb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.

Transparency log

Release files / forgetted-0.3.0-py3-none-any.whl

Download URL forgetted-0.3.0-py3-none-any.whl
Size 23.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5e569f3f1c23fcfc40047bcd5d0794932bc85bb13895a226a6afdbb25a1d3a2c
BLAKE2b-256 checksum
How to use checksums
330145db2398cb8e16d133488c2ebf859f6267fe87e33b0f9f4e360101023163
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.2

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page