Session memory for AI coding agents — event store and projection engine for LLM agent sessions
Project description
wake
Agentic Unified Reduction of Agentic Logs
An event store and projection engine for LLM agent sessions. Captures every tool call, decision, constraint, and blocker as an immutable event, then reduces that history into a structured context snapshot that the next session reads automatically.
The Problem
LLM agents that work across multiple sessions lose context at every boundary. The common workaround — a hand-edited PROGRESS.md — is fragile:
- Agents overwrite their own history
- Important decisions are buried under noise
- There is no way to distinguish "the agent decided X" from "the agent ran
grep" - Context windows fill with raw logs instead of actionable summaries
The Solution
wake implements a Stream + Projection pattern:
Hook fires → Event classified → Event Store → Reducer → ProjectStatus → .wake/projection.md
↑ ↓
MCP Smart Events CLAUDE.md references it
(decisions, blockers, via one-line include
constraints, tasks)
-
Layer 1 (passive) — Every tool call fires a hook. wake classifies the signal level (HIGH/MEDIUM/LOW/DISCARD) and writes it to an append-only SQLite store. The agent doesn't need to cooperate.
-
Layer 2 (active) — The agent calls MCP tools to log decisions, constraints, blockers, and tasks. These "Smart Events" carry the semantic context that tool calls alone can't capture.
-
The Reducer — A pure function that folds events into a
ProjectStatus. No I/O, no side effects, deterministic replay. -
The Renderers —
ProjectStatusis rendered as CLAUDE.md (auto-read by the agent),.wake/directory files, or JSON. Renderers never touch the event store.
Setup
Install
pip install -e /path/to/wake
This provides two commands: wake (CLI) and wake-mcp (MCP server).
Add to a repository
For a Claude-only repo:
wake init --provider claude
For a Codex-only repo:
wake init --provider codex
For a mixed Claude + Codex repo:
wake init --provider both
Agent launchers:
wake claude
wake codex
Claude mode creates .claude/settings.json in the target repo with five hook categories:
| Hook | Trigger | Command |
|---|---|---|
PostToolUse |
After every tool call | wake bridge-send (fast path; falls back to inline ingest) |
SessionStart |
Session begins | wake session-start && (bridge start &) && wake render-projection && cat .wake/projection.md |
Stop |
Session ends | wake bridge stop ; wake session-end && wake extract-smart-events && wake snapshot && wake render-projection && wake render-wake-dir |
PreToolUse (EnterPlanMode) |
Before planning | wake plan-check (injects active decisions into context) |
PreToolUse (Bash) |
Before git commits | Warns if no Smart Events logged this session |
The generated hook commands include export WAKE_STORE="$CLAUDE_PROJECT_DIR/.wake/events.db" WAKE_OUTPUT_DIR="$CLAUDE_PROJECT_DIR" to ensure hooks resolve to the correct project store regardless of the working directory.
The MCP server is registered as:
{
"mcpServers": {
"wake": {
"command": "wake-mcp"
}
}
}
Add to the repo's .gitignore:
.wake/
Start a Claude Code session. The first wake session-start creates .wake/events.db automatically.
For Codex, start sessions through wake codex. It runs Wake startup first, renders
.wake/projection.md and .wake/*.md, prints the projection into startup context,
then launches codex. If WAKE_CODEX_EVENT_CMD is set to a JSONL event source,
wake codex also keeps wake codex-bridge active during the session.
Connecting to CLAUDE.md
wake writes its projection to .wake/projection.md — it never touches your CLAUDE.md. The SessionStart hook outputs the projection content so the agent receives it automatically at session start. For discoverability, add a note to your CLAUDE.md:
This project uses wake for session continuity. Status, constraints, and
decisions are in `.wake/projection.md` and `.wake/*.md`.
What each hook does
| Hook | Trigger | Effect |
|---|---|---|
PostToolUse |
After every tool call | Classifies signal, writes event (DISCARD events dropped). Uses Unix socket bridge for speed; falls back to inline ingest if bridge is unavailable. |
SessionStart |
Session begins | Writes SESSION_STARTED event; starts the bridge daemon in the background; renders .wake/projection.md with POST diagnostics; prints projection to Claude's startup context. |
Stop |
Session ends | Stops bridge; writes SESSION_ENDED event; runs auto-extraction of Smart Events from session history; writes snapshot; renders final projection and .wake/ directory files. |
PreToolUse (EnterPlanMode) |
Before entering plan mode | Outputs all active decisions as a system reminder so the agent cannot begin planning without reviewing prior choices. |
PreToolUse (Bash) |
Before running a shell command | Warns if the session has produced no Smart Events yet, prompting the agent to log decisions and constraints. |
Two-Layer Event Model
Layer 1: Passive hooks (automatic)
Every tool call the agent makes is captured by the PostToolUse hook. A signal classifier assigns each event a level:
| Level | Behavior | Examples |
|---|---|---|
| HIGH | Shown in recent activity | Write, Edit, git commit, pytest, npm install |
| MEDIUM | Compressed ("Consulted 4 files: ...") | First Read of a file, Grep/Glob with no results |
| LOW | Stored but filtered from projection | Repeated reads, Grep/Glob with results |
| DISCARD | Dropped at ingestion, never stored | Re-reading the same file within a session |
Layer 2: Smart Events (agent-initiated)
The MCP server provides 21 tools for logging semantic context and querying state. Each write tool has a CLI equivalent under wake log for when MCP is unavailable:
| MCP Tool | CLI Equivalent | When to use |
|---|---|---|
wake_log_decision |
wake log decision |
Chose between approaches |
wake_log_constraint |
wake log constraint |
Discovered a rule that must be followed |
wake_mark_blocked |
wake log blocked |
Needs human input to proceed |
wake_log_rejection |
wake log rejection |
Tried something and it failed |
wake_log_task_started |
wake log task-start |
Starting a distinct unit of work |
wake_log_task_completed |
wake log task-done |
Finished a unit of work |
wake_log_knowledge |
wake log knowledge |
Log a codebase navigation signpost |
wake_save_checkpoint |
wake log checkpoint |
Save working state before context compaction |
wake_register_event_type |
wake log register-type |
Define a custom event type |
wake_log_custom |
wake log custom |
Log an instance of a custom event type |
wake_note |
wake note |
Quick free-form note (auto-classified) |
wake_review_item |
wake review |
Review a stale item |
wake_update_scope |
wake scope |
Update scope on an item |
wake_get_state |
wake reduce |
Query current project status |
wake_brief |
wake brief |
Execution-time semantic matches |
wake_list |
wake list |
List current semantic items |
wake_get |
wake get |
Get one item by ID |
wake_search |
wake search |
Search items by text |
wake_get_conflicts |
wake conflicts |
Cross-provider conflicts needing resolution |
wake_resolve_conflict |
wake resolve |
Resolve a conflict |
wake_stats |
wake stats |
Prevention and conflict statistics |
The agent's projection (.wake/projection.md) includes both MCP and CLI references, so the agent always knows how to log Smart Events regardless of MCP availability.
Without Smart Events, the next session sees file edits and bash commands but no decisions, constraints, or context.
Architecture
┌─────────────────────────────────────────────────┐
│ Claude Code Session │
│ (or any agent with hook support) │
└──────┬──────────────────────────┬───────────────┘
│ PostToolUse hook │ MCP tool call
│ (Layer 1: passive) │ (Layer 2: active)
▼ ▼
┌─────────────────────────────────────────────────┐
│ StoreAgent (write side) │
│ SignalClassifier → EventStore.append() │
│ SQLite WAL · append-only · immutable triggers │
└──────────────────────┬──────────────────────────┘
│ EventStore.xrange()
▼
┌─────────────────────────────────────────────────┐
│ ProjectionAgent (read side) │
│ wake_reducer() → ProjectStatus → Renderer │
│ pure function · no I/O · deterministic replay │
└──────────────────────┬──────────────────────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
projection.md .wake/*.md JSON
(agent reads) (human reads) (MCP response)
The Reducer Contract
The reducer is a pure left fold over events (see ADR-0002):
- Purity: No I/O, no side effects, no
datetime.now() - Totality: Every event type has a handler
- Idempotence: Same events + same initial state = same output
- Monotonic growth: Facts are appended or retired, never deleted
ProjectStatus
The reducer produces a ProjectStatus — the single object all renderers consume:
class ProjectStatus(BaseModel):
objective: str
constraints: List[Constraint] # rules that must be followed
blocked_on_human: List[Blocker] # open questions
rejected_approaches: List[RejectedApproach] # what not to retry
decisions: List[Decision] # what is settled
active_tasks: List[TaskInfo] # in-progress work
completed_tasks: List[TaskInfo] # finished work
recent_activity: List[str] # compressed tool call log
custom_event_types: List[CustomEventType]
custom_events: List[CustomEvent]
knowledge_map: List[Signpost]
session_log: List[SessionInfo]
Fields are ordered by semantic priority — constraints first (what the agent must never do), activity last (what just happened).
Snapshots
For long-running projects, replaying thousands of events on every session start is wasteful. wake takes snapshots — serialized ProjectStatus at a checkpoint. Resume replays only the events since the snapshot. A reducer version mismatch triggers a full replay automatically.
CLI
Session lifecycle
wake session-start Record session boundary (idempotent)
wake session-end Record session boundary (idempotent)
wake extract-smart-events Auto-extract Smart Events from session history (requires wake[extract])
wake snapshot Checkpoint current ProjectStatus for fast resume
Ingestion
wake ingest Read hook JSON from stdin, classify, store
wake bridge start|stop Manage the Unix socket ingestion daemon
wake bridge-send Send hook JSON to bridge, or fall back to inline ingest
wake codex-bridge Translate Codex event JSON/JSONL to Wake events
Projection and rendering
wake reduce Compute ProjectStatus, print JSON
wake render-projection Render .wake/projection.md with POST checks and delta
wake render-wake-dir Render .wake/ directory files
wake project Time-range projection (--start/--end)
wake plan-check Output active decisions before planning (PreToolUse hook target)
wake post Run power-on self-test diagnostics
Launchers
wake codex Launch Codex with Wake startup rendering
wake claude Launch Claude with Wake startup rendering (symmetry wrapper)
Query and guard
wake brief Execution-time semantic matches (--for-file, --for-command, --for-plan)
wake list [kind] List current semantic items (decisions, constraints, tasks, ...)
wake get <id> Get one semantic item by ID
wake search <text> Search items by text
wake conflicts List material cross-provider conflicts
wake resolve <id> Resolve a conflict (--keep, --mode scope-apart|keep-both)
wake guard-check Evaluate constraints against PreToolUse payload (stdin)
wake read-context Surface signposts after file reads (PostToolUse hook target)
wake rebuild-active-state Rebuild read model from event ledger
Human workflow
wake annotate Record human input (--text, --resolves BLK-NNN)
wake note "..." Log a free-form note; auto-classified as decision/rejection/annotation
wake review <id> Review a stale item (--action agree|revisit|refresh|persist)
wake scope <id> Update the scope path on a decision or constraint (--scope "path")
wake supersede <id> Supersede an item (--by <new-id>)
wake withdraw <id> Withdraw an item from active consideration
wake invalidate <id> Invalidate an item (stale path, outdated knowledge)
wake sweep Find items with dead filesystem paths
wake audit Analyze items for staleness and conflicts
Shared events
wake share-init One-time setup: .gitignore, .gitattributes, backfill
wake share-backfill Export Smart Events to shared JSONL
wake fetch Import teammates' shared events and re-render projections
wake import Import constraints/signposts from stdin JSON
Repository management
wake init Scaffold Claude/Codex integration (--provider claude|codex|both)
wake nohooks Replace Wake-managed hooks with no-op shims for debugging
wake remove Remove all Wake integration from a repository
Other
wake baseline Print codebase exploration prompt for signpost generation
wake collect Register repos for cross-project reporting
wake report Cross-project health report
wake migrate Run pending schema migrations
wake db-version Show current and latest schema version
wake stats Prevention, conflict, and coverage statistics
wake codex-guard Evaluate constraints against Codex event payload (stdin)
wake commit-check Warn if no Smart Events logged this session
wake reintegrate Show delegated task findings (--task-id)
wake reintegrate-hook Surface delegated task findings (SubagentStop hook target)
wake init --provider codex|both also manages a wake-owned block in AGENTS.md
that points Codex at wake codex, .wake/projection.md, and the
WAKE_PROVIDER=codex command pattern for semantic logging.
wake log — Smart Events via CLI
Every MCP write tool has a CLI equivalent under wake log. Use these when MCP is unavailable, or for backfilling events from git history.
wake log decision --decision '...' --rationale '...' --rejected '...' (repeatable)
--supersedes <id> (e.g. d-013, to explicitly retire a prior decision)
wake log constraint --rule '...' --scope '...' --reason '...' --severity hard|soft
wake log blocked --question '...' --context '...'
wake log rejection --approach '...' --reason '...' --context '...'
wake log task-start --description '...' --task-id '...' --parent-id '...'
wake log task-done --task-id '...' --description '...'
wake log register-type --name '...' --description '...' --when-to-log '...'
wake log custom --type '...' --summary '...' --details '...'
wake log knowledge --topic '...' --summary '...' --files '...'
wake log checkpoint --summary '...' --next-steps '...' --files '...'
All subcommands exit 0, even on errors. Hooks that exit non-zero block the agent.
Decision Supersession
Every decision gets a stable ID (d-001, d-042, etc.) assigned at write time. Decisions persist in .wake/decisions.md, split into Active and Superseded sections.
When a decision changes, log the new one with --supersedes:
wake log decision \
--decision "Use custom agents instead of LangGraph" \
--rationale "Simpler control flow for our use case" \
--rejected "LangGraph" \
--supersedes d-013
The prior decision (d-013) is marked superseded in the reducer. decisions.md shows the full chain. projection.md shows only active decisions, with a count of superseded ones at the bottom.
Conflict detection: logging a decision that shares keywords with an existing active decision emits a warning on stderr (advisory, not blocking):
⚠️ POTENTIAL CONFLICT with existing decision:
d-013: "LangGraph + PydanticAI as orchestration stack"
Decided: 2026-02-21
Status: active
If this replaces that decision, re-run with: --supersedes d-013
Threshold configurable via WAKE_CONFLICT_THRESHOLD env var (float 0.0–1.0, default 0.3).
wake plan-check — PreToolUse Hook for Planning
wake plan-check outputs all active decisions with an instructional header designed to be injected into the agent's context before it enters plan mode:
# DECISIONS CHECK — Read before planning
You MUST check each architectural choice against these prior decisions.
If your plan contradicts any of them, STOP and discuss with the user first.
## Active Decisions (3)
### d-013 | 2026-02-21 — LangGraph + PydanticAI as orchestration stack
...
Wire it to EnterPlanMode in .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "EnterPlanMode",
"hooks": [
{ "type": "command", "command": "wake plan-check" }
]
}
]
}
}
The agent literally cannot enter plan mode without seeing all active decisions. The hook output is injected as a system-reminder, so it doesn't consume the agent's response budget.
Store resolution: WAKE_STORE env var, or .wake/events.db (SQLite, default). Paths ending in .jsonl use the JSONL backend.
POST (Power-On Self-Test)
At session start, wake prepends a diagnostics block to .wake/projection.md:
# System Check
- **Schema**: Schema version 1 (up-to-date)
- **MCP**: MCP available (wake-mcp binary, mcp module)
- **Store**: Store has 715 events
- **Smart Events**: Previous session had 29 events and 5 Smart Events
- **Custom Types**: No custom event types registered
POST warns if the previous session had 0 Smart Events — meaning the agent didn't log any decisions, constraints, or blockers. This is the canary for a session that produces tool call noise but no semantic context.
Backfilling an Existing Project
For projects that already have git history but an empty event store, wake includes a backfill prompt that guides the agent through mining git history for decisions, constraints, rejected approaches, tasks, and dependencies.
From a Claude session in the target repo:
Read ~/projects/wake/dev/backfill-prompt.md and follow its instructions
The prompt walks through 7 phases, using wake log CLI commands to populate the event store. It's idempotent — safe to re-run on a previously backfilled repo. The agent checks existing state first and skips duplicates.
Baseline: Codebase Exploration
While backfill mines git history for temporal knowledge (decisions, constraints, rejections), baseline mines the live codebase for spatial knowledge — where things are, how subsystems connect, what's non-obvious.
The baseline prompt guides an agent through structured exploration, logging signposts via wake log knowledge. Each signpost is ~30 words that replace thousands of tokens of re-exploration in future sessions.
# Print the baseline prompt to stdout
wake baseline
# Or read it directly
cat dev/baseline-prompt.md
The prompt is idempotent — it checks existing signposts first and skips topics already covered. Results appear in .wake/knowledge.md and as a pointer in .wake/projection.md.
Verifying the Pipeline
After a few sessions, check that events are flowing:
# Event counts by type
sqlite3 .wake/events.db "SELECT type, COUNT(*) FROM events GROUP BY type ORDER BY COUNT(*) DESC;"
# Smart Events specifically
sqlite3 .wake/events.db \
"SELECT type, payload FROM events \
WHERE type NOT IN ('tool_called','snapshot','session_started','session_ended');"
# Reduced state as JSON
wake reduce
# Rendered projection
cat .wake/projection.md
Documentation
Browse the full documentation locally:
pip install -e ".[docs]"
mkdocs serve
Or read the markdown directly in docs/.
Reference
- CLI Reference — all commands, options, and environment variables
- MCP Reference — all MCP tools and resources with parameters
ADRs
| ADR | Decision |
|---|---|
| 0001 | SQLite WAL as primary backend (JSONL as fallback) |
| 0002 | Reducer purity, snapshot portability, bounded output |
| 0003 | Signal classification at ingestion time, not in reducer |
| 0004 | All sections always emitted, even when empty |
| 0005 | Commit rendered projections for cloud session continuity |
License
Apache 2.0. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wake_memory-0.5.0.tar.gz.
File metadata
- Download URL: wake_memory-0.5.0.tar.gz
- Upload date:
- Size: 258.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60d6e6acf8bf783f8e58b20bea27d052670db80db1e340da67a532418080944e
|
|
| MD5 |
d48ea0e50baaef11ff0d3dda4d40550e
|
|
| BLAKE2b-256 |
e04cfd11dd6ced1f368686743e1e112ef977f25e10aafb6647011ad17cd0df5b
|
File details
Details for the file wake_memory-0.5.0-py3-none-any.whl.
File metadata
- Download URL: wake_memory-0.5.0-py3-none-any.whl
- Upload date:
- Size: 151.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0515054e7ac8988d0ab179656eb26ebcb8bc5d3dccad4ac3c33459a6fbaaede2
|
|
| MD5 |
c91087113eb677e751086ddc46dca9fe
|
|
| BLAKE2b-256 |
007c232dc4404f11a65462dc4080ea741ab6b7bc7872a8ad4914aef9a098aa25
|