Metadata-Version: 2.4
Name: tripartite-memory
Version: 0.2.1
Summary: The official AEGIS OS Tripartite Memory SDK for Autonomous Agents.
Author-email: Alva Systems Architecture LLC <founders@alvasystemsarchitecture.com>
License: Apache-2.0
Project-URL: Homepage, https://github.com/lavalabs/aegis-memory
Project-URL: Documentation, https://github.com/lavalabs/aegis-memory#readme
Project-URL: Repository, https://github.com/lavalabs/aegis-memory.git
Keywords: aegis,memory,llm,agents,neo4j,qdrant
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: pydantic>=2.5.0
Requires-Dist: asyncpg>=0.29.0
Requires-Dist: psycopg[binary]>=3.1.0
Requires-Dist: qdrant-client>=1.7.0
Requires-Dist: neo4j>=5.14.0
Requires-Dist: httpx>=0.27.0
Requires-Dist: structlog>=24.1.0
Requires-Dist: cyclonedx-bom>=4.0.0
Requires-Dist: python-dotenv>=1.0.1

# AEGIS Tripartite Memory SDK 🧠
Available on PyPI as **tripartite-memory**

[![PyPI Version](https://img.shields.io/pypi/v/tripartite-memory.svg)](https://pypi.org/project/tripartite-memory/)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-green.svg)](https://www.python.org/)

Most LLM agents fail in the same way: they forget what already happened. They retry failed approaches, ignore system state, and confidently suggest things that already broke production.

This is **AI Amnesia**.

`tripartite-memory` is a unified async Python SDK that gives AI agents persistent, structured memory across three distinct layers. Before an agent takes action, it can answer:
> "Has this failed before?"  
> "What will this impact?"  
> "Is this safe to execute?"

Instead of guessing, it **knows.**

## Memory & Context Optimization ⚡
`tripartite-memory` significantly reduces the cost and improves the performance of running large models:
- **60-80% Token Reduction:** Instead of dumping massive chat histories into the prompt, `recall()` injects only the 3-5 most relevant precedents.
- **VRAM Relief:** By keeping context windows lean, models consume less VRAM (which scales quadratically with sequence length). Run larger models (32B/70B) on consumer-grade hardware.
- **Improved Reasoning:** Providing specific "Hard Constraints" from the Ledger prevents the LLM from making up rules, leading to deterministic and reliable outputs.

## What This Fixes
**Without memory:**
- Agents loop on failed solutions.
- Context windows explode with irrelevant history.
- Risky actions happen without awareness of dependencies.

**With `tripartite-memory`:**
- Agents avoid known failure paths.
- Context stays small and relevant.
- Actions are informed by real system state and "trace the real blast radius."

## The Tripartite Architecture
To make an LLM safe for production, it needs an operating-system-level memory stack:
1. **The Ledger (Postgres):** Immutable state, strict constraints, and audit logs.
2. **The Semantic Engine (Qdrant):** High-dimensional vector search for historical precedents and documentation.
3. **The Capability Graph (Neo4j):** Dependency mapping to understand how modifying Component A impacts System B.

## Installation

```bash
pip install tripartite-memory
```

## Quickstart

**Step 1 — Configure your databases** (copy `.env.example` and fill in your connection strings):

```bash
cp .env.example .env
# Edit .env with your Postgres, Qdrant, Neo4j, and Ollama URLs
```

Or pass credentials directly to the constructor (see below).

**Step 2 — Use the SDK:**

```python
import asyncio
from tripartite_memory.core import MemoryCore

async def main():
    # Option A: reads POSTGRES_URL, QDRANT_URL, NEO4J_URI from .env
    memory = MemoryCore()

    # Option B: pass credentials explicitly
    # memory = MemoryCore(
    #     postgres_uri="postgresql://user:pass@localhost:5432/mydb",
    #     qdrant_url="http://localhost:6333",
    #     neo4j_uri="bolt://localhost:7687",
    # )

    # 1. Unified Ingestion (Write to all 3 databases simultaneously)
    await memory.ingest(
        content="Modified the Nginx reverse proxy to route /api/v2 traffic to staging.",
        actor="agent:InfrastructureOps",
        tags=["nginx", "networking", "staging"]
    )

    # 2. Pre-Action Context Check (The Blast Radius)
    # Give your agent complete situational awareness before it touches production.
    context = await memory.recall(
        intent="Restart the Nginx service to apply new SSL certificates.",
        graph_depth=2,
        # collection="operator_context",  # override default collection
        # max_age_days=90,                # ignore memories older than 90 days
        # authorized_ring=2,              # ring-level access control (0=highest, 3=public)
    )

    print(context.status)               # "KNOWN", "ADJACENT", "UNKNOWN", or "DEGRADED"
    print(context.blast_radius)         # Neo4j dependent nodes
    print(context.historical_precedents) # Qdrant vector matches
    print(context.authorized_ring)      # ring level used for this query
    print(context.metadata["failed_engines"])  # [] or ["ledger", "semantic", etc.]

if __name__ == "__main__":
    asyncio.run(main())
```

> **Note:** `MemoryCore()` validates that all three database URLs are available at construction time. If any are missing it raises `ValueError` immediately — it does not defer until first use. Ensure your `.env` is loaded or credentials are passed before instantiating.

## The Agent Protocol 🛡️
`tripartite-memory` works best when the agent is "forced" to use it. I recommend adding a **Memory Protocol** to your agent's system prompt. See [SYSTEM_PROMPT.md](./SYSTEM_PROMPT.md) for the exact snippet.

## Universal Integration
- **Local Models (Ollama/LM Studio):** Inject the `recall()` JSON directly into the context window before the user's prompt.
- **CLI Clients (Claude Code/Gemini CLI):** Wrap the SDK in a tool or use the provided **Bridge Script**.

## Bi-directional Memory Bridge 🔄
A ready-to-use bridge is included in [examples/bridge.py](./examples/bridge.py) that works on Linux, Mac, and Windows.

```bash
# Get Context
python examples/bridge.py recall "How do I optimize VRAM on Pascal?"

# Store Knowledge
python examples/bridge.py ingest "Successfully tuned batch size to 4 for Qwen-32B." --tags optimization
```

## Remote Connection Guide (LAN) 🌐
If testing from a remote machine, point the SDK to your server's IP in your `.env`:
```ini
POSTGRES_URL=postgresql://user:password@10.0.0.100:5432/aegis_local
QDRANT_URL=http://10.0.0.100:6333
NEO4J_URI=bolt://10.0.0.100:7687
NEO4J_PASSWORD=your-secure-password
OLLAMA_URL=http://10.0.0.100:11434
```

## Managed Cloud Support ☁️
`tripartite-memory` is compatible with major managed database providers. Just update your `.env` with the cloud connection strings:

- **Vector (Qdrant):** Works with [Qdrant Cloud](https://qdrant.tech/cloud/). Set `QDRANT_API_KEY` in your environment.
- **Graph (Neo4j):** Works with [Neo4j AuraDB](https://neo4j.com/cloud/aura/). Use your provided `bolt://` URI and password.
- **Ledger (Postgres):** Works with [Neon](https://neon.tech/) or [Supabase](https://supabase.com/).

```ini
# Cloud Example
QDRANT_URL=https://your-cluster.qdrant.tech
QDRANT_API_KEY=your-api-key
NEO4J_URI=bolt+s://your-instance.databases.neo4j.io
```

## Injection Guard 🛡️

`tripartite_memory.guards.InjectionGuard` is a zero-dependency text scanner for detecting prompt injection and shell command injection patterns in LLM agent pipelines. Use it to validate user input, inter-agent messages, or any text before it reaches a model or tool executor.

```python
from tripartite_memory.guards import InjectionGuard

# Quick boolean check
if not InjectionGuard.is_safe(user_input):
    raise ValueError("Input rejected by injection guard")

# Full report
result = InjectionGuard.scan_text_for_injection(user_input)
# {
#   "score": 50,           # 0 = clean, ≥50 = high-risk
#   "findings": [{"severity": "HIGH", "description": "...", "pattern": "..."}],
#   "summary": "Injection scan score: 50. 1 finding(s)."
# }
```

**What it detects (HIGH risk, score +50 each):**
- `shell=True` in subprocess, `os.system()`, `eval()`, `exec()`
- Container privilege escalation (`--privileged`, `cap_add SYS_ADMIN`)
- Prompt override attempts (`ignore previous instructions`, `act as`, etc.)
- HTML/JS injection (`<script>`, `javascript:`)
- Malicious shell commands (`rm -rf`, `sudo`, `wget`, `curl`, etc.)
- Template injection with shell operators (`${foo;rm -rf}`)
- Backtick shell execution (`` `rm -rf /` `` — not triggered by markdown inline code)

**Medium risk (score +10 each):** security TODOs, `DEBUG=True`, logical operator chaining.

Scores cap at 100. Pure stdlib — no install overhead.

## SBOM & Transparency

This repository includes a **Software Bill of Materials (SBOM)** in CycloneDX format.
- **View SBOM:** [sbom.json](./sbom.json)
- **Generate Fresh SBOM:** `python scripts/generate_sbom.py`

## Why I Built This
I built this SDK as the foundational memory layer for **AEGIS OS** — a bare-metal AI orchestration system designed to govern AI agents on real infrastructure using deterministic safety tiers (T0/T1/T2).

While the core OS uses a Business Source License (BSL), I believe fundamental agentic memory should be open and standardized. `tripartite-memory` is 100% open-source (Apache 2.0).

Built by **[John Alva](https://alvasystemsarchitecture.com)** — infrastructure and AI automation for organizations that can't afford downtime. | [Alva Systems](https://alvasystemsarchitecture.com)

## Changelog

### v0.2.1
- **Fix:** `authorized_ring` now correctly populated in `ContextPayload` (was always defaulting to 3)
- **New:** `status = "DEGRADED"` when one or more engines fail — callers can now detect partial availability
- **New:** `metadata["failed_engines"]` lists which stores were unavailable on a given recall
- **Fix:** Timeouts now configurable via `TRIPARTITE_OLLAMA_TIMEOUT` and `TRIPARTITE_QDRANT_TIMEOUT` env vars (default 60s/30s)
- **Fix:** `&&`/`||` injection pattern narrowed — logical operators in prose no longer trigger false positives; only shell command-chaining patterns are flagged
- **Fix:** `datetime.utcnow()` replaced with timezone-aware `datetime.now(timezone.utc)` throughout
- **Docs:** Quickstart updated with explicit `.env` setup step, constructor alternatives, and all `recall()` parameters documented

### v0.2.0
- **New:** `tripartite_memory.guards.InjectionGuard` — zero-dependency prompt and shell injection scanner (stdlib only)
- **New:** `InjectionGuard.is_safe(text, threshold)` convenience method
- **Fix:** Narrowed backtick injection pattern — markdown inline code no longer triggers false positive
- **Fix:** Narrowed template literal pattern to only flag when shell operators are embedded (`${foo;rm}` triggers, `${VAR}` does not)
- **Lifecycle:** `nightly_pruning.py` default collections updated to generic names
- **Internal:** `format_as_stable_suffix` header string generalized

### v0.1.4
- Add stable suffix decoding support for prefix caching

### v0.1.3
- Add support for staleness filtering (`max_age_days`)

### v0.1.2
- Code review fixes and hardening

## Contributing
PRs are welcome. If you are building agentic systems that require strict intent multiplexing and deterministic safety, I'd love to collaborate.
