Skip to main content

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)
  1. 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.

  2. 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.

  3. The Reducer — A pure function that folds events into a ProjectStatus. No I/O, no side effects, deterministic replay.

  4. The RenderersProjectStatus is 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wake_memory-0.6.0.tar.gz (258.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wake_memory-0.6.0-py3-none-any.whl (151.0 kB view details)

Uploaded Python 3

File details

Details for the file wake_memory-0.6.0.tar.gz.

File metadata

  • Download URL: wake_memory-0.6.0.tar.gz
  • Upload date:
  • Size: 258.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for wake_memory-0.6.0.tar.gz
Algorithm Hash digest
SHA256 c0e4b8fecbf12540e40f320f6807e48943eee18b55d96c2b19f346b23d30fb71
MD5 c750a6e3fdb555b5f15072ab89fa7f3b
BLAKE2b-256 37c925c593e5aa2eabd05f837165bf53628f693c61bf03a9476651c8e80e2eff

See more details on using hashes here.

Provenance

The following attestation bundles were made for wake_memory-0.6.0.tar.gz:

Publisher: release.yml on coreyt/wake

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file wake_memory-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: wake_memory-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 151.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for wake_memory-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3ef223859f38d51821ef6d4ff5fbb6b2ff62059451ec33193ce22da8819f8206
MD5 f5d0ae7037ac521960ad20547b24242c
BLAKE2b-256 6f39c553f9197e5bcd112a10adfa2c7cd3065c8969f2160ed5cf7916ec850967

See more details on using hashes here.

Provenance

The following attestation bundles were made for wake_memory-0.6.0-py3-none-any.whl:

Publisher: release.yml on coreyt/wake

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page