Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,32 @@
## Configuration & Secrets
- Gemini requires `GEMINI_API_KEY` (or `GOOGLE_API_KEY`). OpenAI uses `OPENAI_API_KEY` via the SDK.
- Never commit secrets; document new environment variables in README or this file when introduced.

<!-- ENGRAM_CONTINUITY:START -->
## Engram Continuity (Auto-Generated)

Follow these rules for cross-agent continuity on every new task/thread.

1) Before answering substantive repo/task questions, call `get_last_session`:
- `user_id`: `"default"` unless provided
- `requester_agent_id`: `"codex"`
- `repo`: absolute workspace path
- Include `agent_id` only when the user explicitly asks to continue from a specific source agent.

2) If no handoff session exists, continue normally and use memory tools as needed.

3) On major milestones and before pausing/ending, call `save_session_digest` with:
- `task_summary`
- `repo`
- `status` (`"active"`, `"paused"`, or `"completed"`)
- `decisions_made`, `files_touched`, `todos_remaining`
- `blockers`, `key_commands`, `test_results` when available
- `agent_id`: `"codex"`, `requester_agent_id`: `"codex"`

4) Prefer Engram MCP handoff tools over shell/SQLite inspection for continuity.

Target agent profile: `Codex/agent-runner`.
<!-- ENGRAM_CONTINUITY:END -->



28 changes: 28 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!-- ENGRAM_CONTINUITY:START -->
## Engram Continuity (Auto-Generated)

Follow these rules for cross-agent continuity on every new task/thread.

1) Before answering substantive repo/task questions, call `get_last_session`:
- `user_id`: `"default"` unless provided
- `requester_agent_id`: `"claude-code"`
- `repo`: absolute workspace path
- Include `agent_id` only when the user explicitly asks to continue from a specific source agent.

2) If no handoff session exists, continue normally and use memory tools as needed.

3) On major milestones and before pausing/ending, call `save_session_digest` with:
- `task_summary`
- `repo`
- `status` (`"active"`, `"paused"`, or `"completed"`)
- `decisions_made`, `files_touched`, `todos_remaining`
- `blockers`, `key_commands`, `test_results` when available
- `agent_id`: `"claude-code"`, `requester_agent_id`: `"claude-code"`

4) Prefer Engram MCP handoff tools over shell/SQLite inspection for continuity.

Target agent profile: `Claude Code`.
<!-- ENGRAM_CONTINUITY:END -->



28 changes: 28 additions & 0 deletions CURSOR.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!-- ENGRAM_CONTINUITY:START -->
## Engram Continuity (Auto-Generated)

Follow these rules for cross-agent continuity on every new task/thread.

1) Before answering substantive repo/task questions, call `get_last_session`:
- `user_id`: `"default"` unless provided
- `requester_agent_id`: `"cursor"`
- `repo`: absolute workspace path
- Include `agent_id` only when the user explicitly asks to continue from a specific source agent.

2) If no handoff session exists, continue normally and use memory tools as needed.

3) On major milestones and before pausing/ending, call `save_session_digest` with:
- `task_summary`
- `repo`
- `status` (`"active"`, `"paused"`, or `"completed"`)
- `decisions_made`, `files_touched`, `todos_remaining`
- `blockers`, `key_commands`, `test_results` when available
- `agent_id`: `"cursor"`, `requester_agent_id`: `"cursor"`

4) Prefer Engram MCP handoff tools over shell/SQLite inspection for continuity.

Target agent profile: `Cursor`.
<!-- ENGRAM_CONTINUITY:END -->



87 changes: 68 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@
</h3>

<p align="center">
A user-owned memory store that any agent can plug into to become instantly personalized.<br>
Agents read via scoped retrieval. Writes land in staging until you approve.
Hit a rate limit in Claude Code? Open Codex — it already knows what you were doing.<br>
One memory kernel. Shared across every agent. Bio-inspired forgetting. Staged writes. Episodic recall.
</p>

<p align="center">
<b>⚠ Early-stage software — not recommended for production use. APIs may change. Use at your own risk.</b>
</p>

<p align="center">
Expand All @@ -36,23 +40,24 @@

## Why Engram

Every AI agent you use starts with amnesia. Your coding assistant forgets your preferences between sessions. Your planning agent has no idea what your research agent discovered yesterday. You end up re-explaining context that should already be known.
Every AI agent you use starts with amnesia. But the real pain isn't just forgetting — it's what happens when you **switch agents**.

**Engram fixes this.** It's a Personal Memory Kernel (PMK) — a single memory store that sits between you and all your agents. Any agent can plug in via MCP or REST to become instantly personalized, without you having to repeat yourself.
You're 40 minutes into a refactor with Claude Code. You've touched six files, picked a migration strategy, mapped out the remaining TODOs. Then you hit a rate limit. Or your terminal crashes. Or you just need Codex for the next part. So you switch — and the new agent has **zero context**. You re-paste file paths, re-explain decisions, re-describe the plan. Half the time the new agent contradicts something you'd already decided.

But unlike "store everything forever" approaches, Engram treats agents as **untrusted writers**. Writes land in staging. You control what sticks. And memories that stop being useful fade away naturally — just like biological memory.
**Engram fixes this.** It's a Personal Memory Kernel (PMK) — one memory store shared across all your agents. When Claude Code pauses, it saves a session digest. When Codex picks up, it loads that digest and continues where you left off. No re-explanation. No cold starts.

| Capability | Other Memory Layers | **Engram** |
|:-----------|:--------------------|:-----------|
| Bio-inspired forgetting | No | **Ebbinghaus decay curve** |
| Untrusted agent writes | Store directly | **Staging + verification + conflict stash** |
| Episodic narrative memory | No | **CAST scenes (time/place/topic)** |
| Multi-modal encoding | Rare | **5 retrieval paths (EchoMem)** |
| Cross-agent memory sharing | Per-agent silos | **Scoped retrieval with masking** |
| Knowledge graph | Sometimes | **Entity extraction + linking** |
But Engram isn't just a handoff bus. It solves four fundamental problems with how AI memory works today:

| Problem | Other Memory Layers | **Engram** |
|:--------|:--------------------|:-----------|
| **Switching agents = cold start** | Manual copy/paste context | **Handoff bus — session digests, auto-resume** |
| **Nobody forgets** | Store everything forever | **Ebbinghaus decay curve, ~45% less storage** |
| **Agents write with no oversight** | Store directly | **Staging + verification + trust scoring** |
| **No episodic memory** | Vector search only | **CAST scenes (time/place/topic)** |
| Multi-modal encoding | Single embedding | **5 retrieval paths (EchoMem)** |
| Cross-agent memory sharing | Per-agent silos | **Scoped retrieval with all-but-mask privacy** |
| Reference-aware decay | No | **If other agents use it, don't delete it** |
| Hybrid search | Vector only | **Semantic + keyword + episodic** |
| Storage efficiency | Store everything | **~45% less** |
| Knowledge graph | Sometimes | **Entity extraction + linking** |
| MCP + REST | One or the other | **Both, plug-and-play** |
| Local-first | Cloud-required | **127.0.0.1:8100 by default** |

Expand Down Expand Up @@ -127,9 +132,10 @@ docker compose up -d # API at http://localhost:8100

Engram is a **Personal Memory Kernel** — not just a vector store with an API. It has opinions about how memory should work:

1. **Agents are untrusted writers.** Every write is a proposal that lands in staging. Trusted agents can auto-merge; untrusted ones wait for approval.
1. **Switching agents shouldn't mean starting over.** When an agent pauses — rate limit, crash, tool switch — it saves a session digest. The next agent loads it and continues. Zero re-explanation.
2. **Memory has a lifecycle.** New memories start in short-term (SML), get promoted to long-term (LML) through repeated access, and fade away through Ebbinghaus decay if unused.
3. **Scoping is mandatory.** Every memory is scoped by user. Agents see only what they're allowed to — everything else gets the "all but mask" treatment (structure visible, details redacted).
3. **Agents are untrusted writers.** Every write is a proposal that lands in staging. Trusted agents can auto-merge; untrusted ones wait for approval.
4. **Scoping is mandatory.** Every memory is scoped by user. Agents see only what they're allowed to — everything else gets the "all but mask" treatment (structure visible, details redacted).

```
┌─────────────────────────────────────────────────────────────────┐
Expand Down Expand Up @@ -192,7 +198,7 @@ Engram is a **Personal Memory Kernel** — not just a vector store with an API.

### The Memory Stack

Engram combines four bio-inspired memory systems, each handling a different aspect of how humans actually remember:
Engram combines five systems, each handling a different aspect of how memory should work:

#### FadeMem — Decay & Consolidation

Expand Down Expand Up @@ -243,6 +249,21 @@ Scene: "Engram v2 architecture session"
Memories: [mem_1, mem_2] ← semantic facts extracted
```

#### Handoff Bus — Cross-Agent Continuity

When an agent pauses work — rate limit, crash, you switch tools — it saves a session digest: task summary, decisions made, files touched, remaining TODOs, blockers. The next agent calls `get_last_session` and gets the full context. No re-explanation needed.

```
Claude Code (rate limited)
→ save_session_digest(task, decisions, files, todos, blockers)
→ Session stored in handoff bus

Codex (picks up)
→ get_last_session(repo="/my-project")
→ Gets full context: task, decisions, files, todos
→ Continues where Claude Code stopped
```

---

### Key Flows
Expand Down Expand Up @@ -300,6 +321,16 @@ Engram is plug-and-play. Run `engram install` and it auto-configures everything:
engram install # Writes MCP config to ~/.claude.json
```

`engram install` also bootstraps workspace continuity rules (in your current
project directory) so agents call handoff tools automatically:

- `AGENTS.md`
- `CLAUDE.md`
- `CURSOR.md`
- `.cursor/rules/engram-continuity.mdc`

Set `ENGRAM_INSTALL_SKIP_WORKSPACE_RULES=1` to disable this behavior.

**MCP tools** give Claude reactive memory — it stores and retrieves when you ask.

The optional **Claude Code plugin** makes memory **proactive** — relevant context is injected automatically before Claude sees your message:
Expand Down Expand Up @@ -340,10 +371,12 @@ Claude: Based on your preferences, I'd recommend TypeScript...
### Cursor

`engram install` writes MCP config to `~/.cursor/mcp.json`. Restart Cursor to load.
It also sets `ENGRAM_MCP_AGENT_ID=cursor` for deterministic handoff identity.

### OpenAI Codex

`engram install` writes MCP config to `~/.codex/config.toml`. Restart Codex to load.
It also sets `ENGRAM_MCP_AGENT_ID=codex` for deterministic handoff identity.

### OpenClaw

Expand Down Expand Up @@ -379,6 +412,9 @@ Once configured, your agent has access to these tools:
| `list_pending_commits` | Inspect staged write queue |
| `resolve_conflict` | Resolve invariant conflicts (accept proposed or keep existing) |
| `search_scenes` / `get_scene` | Episodic CAST scene retrieval with masking policy |
| `save_session_digest` | Save handoff context when pausing or switching agents |
| `get_last_session` | Load session context from the last active agent |
| `list_sessions` | Browse handoff history across agents |

---

Expand Down Expand Up @@ -432,6 +468,19 @@ curl "http://localhost:8100/v1/trust?user_id=u123&agent_id=planner"
# 7. Sleep-cycle maintenance
curl -X POST http://localhost:8100/v1/sleep/run \
-d '{"user_id": "u123", "apply_decay": true, "cleanup_stale_refs": true}'

# 8. Zero-intervention handoff (session bus)
curl -X POST http://localhost:8100/v1/handoff/resume \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{"user_id":"u123","agent_id":"frontend","repo_path":"/repo","objective":"continue latest task"}'

curl -X POST http://localhost:8100/v1/handoff/checkpoint \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{"user_id":"u123","agent_id":"frontend","repo_path":"/repo","task_summary":"implemented card layout"}'

curl "http://localhost:8100/v1/handoff/lanes?user_id=u123"
```

### Python SDK
Expand Down Expand Up @@ -796,7 +845,7 @@ MIT License — see [LICENSE](LICENSE) for details.
---

<p align="center">
<b>Your agents forget everything between sessions. Engram fixes that.</b>
<b>Switch agents without losing context. Stop re-explaining yourself.</b>
<br><br>
<a href="https://github.com/Ashish-dwi99/Engram">GitHub</a> &middot;
<a href="https://github.com/Ashish-dwi99/Engram/issues">Issues</a> &middot;
Expand Down
92 changes: 92 additions & 0 deletions engram/api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
CommitResolutionRequest,
ConflictResolutionRequest,
DailyDigestResponse,
HandoffCheckpointRequest,
HandoffResumeRequest,
NamespaceDeclareRequest,
NamespacePermissionRequest,
SceneSearchRequest,
Expand Down Expand Up @@ -178,6 +180,96 @@ async def create_session(request: SessionCreateRequest, http_request: Request):
raise HTTPException(status_code=403, detail=str(exc))


@app.post("/v1/handoff/resume")
@app.post("/v1/handoff/resume/")
async def handoff_resume(request: HandoffResumeRequest, http_request: Request):
token = get_token_from_request(http_request)
kernel = get_kernel()
try:
return kernel.auto_resume_context(
user_id=request.user_id,
agent_id=request.agent_id,
repo_path=request.repo_path,
branch=request.branch,
lane_type=request.lane_type,
objective=request.objective,
agent_role=request.agent_role,
namespace=request.namespace,
statuses=request.statuses,
auto_create=request.auto_create,
token=token,
requester_agent_id=request.requester_agent_id,
)
except PermissionError as exc:
raise require_session_error(exc)


@app.post("/v1/handoff/checkpoint")
@app.post("/v1/handoff/checkpoint/")
async def handoff_checkpoint(request: HandoffCheckpointRequest, http_request: Request):
token = get_token_from_request(http_request)
kernel = get_kernel()
payload = {
"status": request.status,
"task_summary": request.task_summary,
"decisions_made": request.decisions_made,
"files_touched": request.files_touched,
"todos_remaining": request.todos_remaining,
"blockers": request.blockers,
"key_commands": request.key_commands,
"test_results": request.test_results,
"context_snapshot": request.context_snapshot,
}
try:
return kernel.auto_checkpoint(
user_id=request.user_id,
agent_id=request.agent_id,
payload=payload,
event_type=request.event_type,
repo_path=request.repo_path,
branch=request.branch,
lane_id=request.lane_id,
lane_type=request.lane_type,
objective=request.objective,
agent_role=request.agent_role,
namespace=request.namespace,
confidentiality_scope=request.confidentiality_scope,
expected_version=request.expected_version,
token=token,
requester_agent_id=request.requester_agent_id,
)
except PermissionError as exc:
raise require_session_error(exc)


@app.get("/v1/handoff/lanes")
@app.get("/v1/handoff/lanes/")
async def list_handoff_lanes(
http_request: Request,
user_id: str = Query(default="default"),
repo_path: Optional[str] = Query(default=None),
status: Optional[str] = Query(default=None),
statuses: Optional[List[str]] = Query(default=None),
limit: int = Query(default=20, ge=1, le=200),
requester_agent_id: Optional[str] = Query(default=None),
):
token = get_token_from_request(http_request)
kernel = get_kernel()
try:
lanes = kernel.list_handoff_lanes(
user_id=user_id,
repo_path=repo_path,
status=status,
statuses=statuses,
limit=limit,
token=token,
requester_agent_id=requester_agent_id,
)
return {"lanes": lanes, "count": len(lanes)}
except PermissionError as exc:
raise require_session_error(exc)


@app.post("/v1/search", response_model=SearchResultResponse)
@app.post("/v1/search/", response_model=SearchResultResponse)
@app.post("/v1/memories/search", response_model=SearchResultResponse)
Expand Down
Loading
Loading