Skip to content

Commit 888ba83

Browse files
Ashish-dwi99claude
andcommitted
feat: cross-agent handoff bus, updated positioning
- Add HandoffSessionBus with session digests, auto-resume, lane checkpointing - Add handoff MCP tools: save_session_digest, get_last_session, list_sessions - Update README to lead with agent-switching pain point and 4-pillar positioning - Add early-stage warning to README — not recommended for production use - Add workspace continuity rules (CLAUDE.md, CURSOR.md, AGENTS.md) - Extend CLI installer, API routes, DB schema for handoff support Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 84df0df commit 888ba83

18 files changed

Lines changed: 3423 additions & 32 deletions

File tree

AGENTS.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,32 @@
3030
## Configuration & Secrets
3131
- Gemini requires `GEMINI_API_KEY` (or `GOOGLE_API_KEY`). OpenAI uses `OPENAI_API_KEY` via the SDK.
3232
- Never commit secrets; document new environment variables in README or this file when introduced.
33+
34+
<!-- ENGRAM_CONTINUITY:START -->
35+
## Engram Continuity (Auto-Generated)
36+
37+
Follow these rules for cross-agent continuity on every new task/thread.
38+
39+
1) Before answering substantive repo/task questions, call `get_last_session`:
40+
- `user_id`: `"default"` unless provided
41+
- `requester_agent_id`: `"codex"`
42+
- `repo`: absolute workspace path
43+
- Include `agent_id` only when the user explicitly asks to continue from a specific source agent.
44+
45+
2) If no handoff session exists, continue normally and use memory tools as needed.
46+
47+
3) On major milestones and before pausing/ending, call `save_session_digest` with:
48+
- `task_summary`
49+
- `repo`
50+
- `status` (`"active"`, `"paused"`, or `"completed"`)
51+
- `decisions_made`, `files_touched`, `todos_remaining`
52+
- `blockers`, `key_commands`, `test_results` when available
53+
- `agent_id`: `"codex"`, `requester_agent_id`: `"codex"`
54+
55+
4) Prefer Engram MCP handoff tools over shell/SQLite inspection for continuity.
56+
57+
Target agent profile: `Codex/agent-runner`.
58+
<!-- ENGRAM_CONTINUITY:END -->
59+
60+
61+

CLAUDE.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<!-- ENGRAM_CONTINUITY:START -->
2+
## Engram Continuity (Auto-Generated)
3+
4+
Follow these rules for cross-agent continuity on every new task/thread.
5+
6+
1) Before answering substantive repo/task questions, call `get_last_session`:
7+
- `user_id`: `"default"` unless provided
8+
- `requester_agent_id`: `"claude-code"`
9+
- `repo`: absolute workspace path
10+
- Include `agent_id` only when the user explicitly asks to continue from a specific source agent.
11+
12+
2) If no handoff session exists, continue normally and use memory tools as needed.
13+
14+
3) On major milestones and before pausing/ending, call `save_session_digest` with:
15+
- `task_summary`
16+
- `repo`
17+
- `status` (`"active"`, `"paused"`, or `"completed"`)
18+
- `decisions_made`, `files_touched`, `todos_remaining`
19+
- `blockers`, `key_commands`, `test_results` when available
20+
- `agent_id`: `"claude-code"`, `requester_agent_id`: `"claude-code"`
21+
22+
4) Prefer Engram MCP handoff tools over shell/SQLite inspection for continuity.
23+
24+
Target agent profile: `Claude Code`.
25+
<!-- ENGRAM_CONTINUITY:END -->
26+
27+
28+

CURSOR.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<!-- ENGRAM_CONTINUITY:START -->
2+
## Engram Continuity (Auto-Generated)
3+
4+
Follow these rules for cross-agent continuity on every new task/thread.
5+
6+
1) Before answering substantive repo/task questions, call `get_last_session`:
7+
- `user_id`: `"default"` unless provided
8+
- `requester_agent_id`: `"cursor"`
9+
- `repo`: absolute workspace path
10+
- Include `agent_id` only when the user explicitly asks to continue from a specific source agent.
11+
12+
2) If no handoff session exists, continue normally and use memory tools as needed.
13+
14+
3) On major milestones and before pausing/ending, call `save_session_digest` with:
15+
- `task_summary`
16+
- `repo`
17+
- `status` (`"active"`, `"paused"`, or `"completed"`)
18+
- `decisions_made`, `files_touched`, `todos_remaining`
19+
- `blockers`, `key_commands`, `test_results` when available
20+
- `agent_id`: `"cursor"`, `requester_agent_id`: `"cursor"`
21+
22+
4) Prefer Engram MCP handoff tools over shell/SQLite inspection for continuity.
23+
24+
Target agent profile: `Cursor`.
25+
<!-- ENGRAM_CONTINUITY:END -->
26+
27+
28+

README.md

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@
1111
</h3>
1212

1313
<p align="center">
14-
A user-owned memory store that any agent can plug into to become instantly personalized.<br>
15-
Agents read via scoped retrieval. Writes land in staging until you approve.
14+
Hit a rate limit in Claude Code? Open Codex — it already knows what you were doing.<br>
15+
One memory kernel. Shared across every agent. Bio-inspired forgetting. Staged writes. Episodic recall.
16+
</p>
17+
18+
<p align="center">
19+
<b>⚠ Early-stage software — not recommended for production use. APIs may change. Use at your own risk.</b>
1620
</p>
1721

1822
<p align="center">
@@ -36,23 +40,24 @@
3640

3741
## Why Engram
3842

39-
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.
43+
Every AI agent you use starts with amnesia. But the real pain isn't just forgetting — it's what happens when you **switch agents**.
4044

41-
**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.
45+
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.
4246

43-
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.
47+
**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.
4448

45-
| Capability | Other Memory Layers | **Engram** |
46-
|:-----------|:--------------------|:-----------|
47-
| Bio-inspired forgetting | No | **Ebbinghaus decay curve** |
48-
| Untrusted agent writes | Store directly | **Staging + verification + conflict stash** |
49-
| Episodic narrative memory | No | **CAST scenes (time/place/topic)** |
50-
| Multi-modal encoding | Rare | **5 retrieval paths (EchoMem)** |
51-
| Cross-agent memory sharing | Per-agent silos | **Scoped retrieval with masking** |
52-
| Knowledge graph | Sometimes | **Entity extraction + linking** |
49+
But Engram isn't just a handoff bus. It solves four fundamental problems with how AI memory works today:
50+
51+
| Problem | Other Memory Layers | **Engram** |
52+
|:--------|:--------------------|:-----------|
53+
| **Switching agents = cold start** | Manual copy/paste context | **Handoff bus — session digests, auto-resume** |
54+
| **Nobody forgets** | Store everything forever | **Ebbinghaus decay curve, ~45% less storage** |
55+
| **Agents write with no oversight** | Store directly | **Staging + verification + trust scoring** |
56+
| **No episodic memory** | Vector search only | **CAST scenes (time/place/topic)** |
57+
| Multi-modal encoding | Single embedding | **5 retrieval paths (EchoMem)** |
58+
| Cross-agent memory sharing | Per-agent silos | **Scoped retrieval with all-but-mask privacy** |
5359
| Reference-aware decay | No | **If other agents use it, don't delete it** |
54-
| Hybrid search | Vector only | **Semantic + keyword + episodic** |
55-
| Storage efficiency | Store everything | **~45% less** |
60+
| Knowledge graph | Sometimes | **Entity extraction + linking** |
5661
| MCP + REST | One or the other | **Both, plug-and-play** |
5762
| Local-first | Cloud-required | **127.0.0.1:8100 by default** |
5863

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

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

130-
1. **Agents are untrusted writers.** Every write is a proposal that lands in staging. Trusted agents can auto-merge; untrusted ones wait for approval.
135+
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.
131136
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.
132-
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).
137+
3. **Agents are untrusted writers.** Every write is a proposal that lands in staging. Trusted agents can auto-merge; untrusted ones wait for approval.
138+
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).
133139

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

193199
### The Memory Stack
194200

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

197203
#### FadeMem — Decay & Consolidation
198204

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

252+
#### Handoff Bus — Cross-Agent Continuity
253+
254+
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.
255+
256+
```
257+
Claude Code (rate limited)
258+
→ save_session_digest(task, decisions, files, todos, blockers)
259+
→ Session stored in handoff bus
260+
261+
Codex (picks up)
262+
→ get_last_session(repo="/my-project")
263+
→ Gets full context: task, decisions, files, todos
264+
→ Continues where Claude Code stopped
265+
```
266+
246267
---
247268

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

324+
`engram install` also bootstraps workspace continuity rules (in your current
325+
project directory) so agents call handoff tools automatically:
326+
327+
- `AGENTS.md`
328+
- `CLAUDE.md`
329+
- `CURSOR.md`
330+
- `.cursor/rules/engram-continuity.mdc`
331+
332+
Set `ENGRAM_INSTALL_SKIP_WORKSPACE_RULES=1` to disable this behavior.
333+
303334
**MCP tools** give Claude reactive memory — it stores and retrieves when you ask.
304335

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

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

344376
### OpenAI Codex
345377

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

348381
### OpenClaw
349382

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

383419
---
384420

@@ -432,6 +468,19 @@ curl "http://localhost:8100/v1/trust?user_id=u123&agent_id=planner"
432468
# 7. Sleep-cycle maintenance
433469
curl -X POST http://localhost:8100/v1/sleep/run \
434470
-d '{"user_id": "u123", "apply_decay": true, "cleanup_stale_refs": true}'
471+
472+
# 8. Zero-intervention handoff (session bus)
473+
curl -X POST http://localhost:8100/v1/handoff/resume \
474+
-H "Authorization: Bearer <TOKEN>" \
475+
-H "Content-Type: application/json" \
476+
-d '{"user_id":"u123","agent_id":"frontend","repo_path":"/repo","objective":"continue latest task"}'
477+
478+
curl -X POST http://localhost:8100/v1/handoff/checkpoint \
479+
-H "Authorization: Bearer <TOKEN>" \
480+
-H "Content-Type: application/json" \
481+
-d '{"user_id":"u123","agent_id":"frontend","repo_path":"/repo","task_summary":"implemented card layout"}'
482+
483+
curl "http://localhost:8100/v1/handoff/lanes?user_id=u123"
435484
```
436485

437486
### Python SDK
@@ -796,7 +845,7 @@ MIT License — see [LICENSE](LICENSE) for details.
796845
---
797846

798847
<p align="center">
799-
<b>Your agents forget everything between sessions. Engram fixes that.</b>
848+
<b>Switch agents without losing context. Stop re-explaining yourself.</b>
800849
<br><br>
801850
<a href="https://github.com/Ashish-dwi99/Engram">GitHub</a> &middot;
802851
<a href="https://github.com/Ashish-dwi99/Engram/issues">Issues</a> &middot;

engram/api/app.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
CommitResolutionRequest,
2929
ConflictResolutionRequest,
3030
DailyDigestResponse,
31+
HandoffCheckpointRequest,
32+
HandoffResumeRequest,
3133
NamespaceDeclareRequest,
3234
NamespacePermissionRequest,
3335
SceneSearchRequest,
@@ -178,6 +180,96 @@ async def create_session(request: SessionCreateRequest, http_request: Request):
178180
raise HTTPException(status_code=403, detail=str(exc))
179181

180182

183+
@app.post("/v1/handoff/resume")
184+
@app.post("/v1/handoff/resume/")
185+
async def handoff_resume(request: HandoffResumeRequest, http_request: Request):
186+
token = get_token_from_request(http_request)
187+
kernel = get_kernel()
188+
try:
189+
return kernel.auto_resume_context(
190+
user_id=request.user_id,
191+
agent_id=request.agent_id,
192+
repo_path=request.repo_path,
193+
branch=request.branch,
194+
lane_type=request.lane_type,
195+
objective=request.objective,
196+
agent_role=request.agent_role,
197+
namespace=request.namespace,
198+
statuses=request.statuses,
199+
auto_create=request.auto_create,
200+
token=token,
201+
requester_agent_id=request.requester_agent_id,
202+
)
203+
except PermissionError as exc:
204+
raise require_session_error(exc)
205+
206+
207+
@app.post("/v1/handoff/checkpoint")
208+
@app.post("/v1/handoff/checkpoint/")
209+
async def handoff_checkpoint(request: HandoffCheckpointRequest, http_request: Request):
210+
token = get_token_from_request(http_request)
211+
kernel = get_kernel()
212+
payload = {
213+
"status": request.status,
214+
"task_summary": request.task_summary,
215+
"decisions_made": request.decisions_made,
216+
"files_touched": request.files_touched,
217+
"todos_remaining": request.todos_remaining,
218+
"blockers": request.blockers,
219+
"key_commands": request.key_commands,
220+
"test_results": request.test_results,
221+
"context_snapshot": request.context_snapshot,
222+
}
223+
try:
224+
return kernel.auto_checkpoint(
225+
user_id=request.user_id,
226+
agent_id=request.agent_id,
227+
payload=payload,
228+
event_type=request.event_type,
229+
repo_path=request.repo_path,
230+
branch=request.branch,
231+
lane_id=request.lane_id,
232+
lane_type=request.lane_type,
233+
objective=request.objective,
234+
agent_role=request.agent_role,
235+
namespace=request.namespace,
236+
confidentiality_scope=request.confidentiality_scope,
237+
expected_version=request.expected_version,
238+
token=token,
239+
requester_agent_id=request.requester_agent_id,
240+
)
241+
except PermissionError as exc:
242+
raise require_session_error(exc)
243+
244+
245+
@app.get("/v1/handoff/lanes")
246+
@app.get("/v1/handoff/lanes/")
247+
async def list_handoff_lanes(
248+
http_request: Request,
249+
user_id: str = Query(default="default"),
250+
repo_path: Optional[str] = Query(default=None),
251+
status: Optional[str] = Query(default=None),
252+
statuses: Optional[List[str]] = Query(default=None),
253+
limit: int = Query(default=20, ge=1, le=200),
254+
requester_agent_id: Optional[str] = Query(default=None),
255+
):
256+
token = get_token_from_request(http_request)
257+
kernel = get_kernel()
258+
try:
259+
lanes = kernel.list_handoff_lanes(
260+
user_id=user_id,
261+
repo_path=repo_path,
262+
status=status,
263+
statuses=statuses,
264+
limit=limit,
265+
token=token,
266+
requester_agent_id=requester_agent_id,
267+
)
268+
return {"lanes": lanes, "count": len(lanes)}
269+
except PermissionError as exc:
270+
raise require_session_error(exc)
271+
272+
181273
@app.post("/v1/search", response_model=SearchResultResponse)
182274
@app.post("/v1/search/", response_model=SearchResultResponse)
183275
@app.post("/v1/memories/search", response_model=SearchResultResponse)

0 commit comments

Comments
 (0)