Metadata-Version: 2.4
Name: wake-memory
Version: 0.5.0
Summary: Session memory for AI coding agents — event store and projection engine for LLM agent sessions
Project-URL: Homepage, https://github.com/coreyt/wake
Project-URL: Repository, https://github.com/coreyt/wake
Project-URL: Issues, https://github.com/coreyt/wake/issues
Author-email: Corey Thompson <coreyt@gmail.com>
License: Apache-2.0
License-File: LICENSE
Keywords: agent,claude,claude-code,context,event-sourcing,llm,mcp,session-memory
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: mcp<2.0,>=1.0
Requires-Dist: pydantic<3.0,>=2.0
Provides-Extra: dev
Requires-Dist: bandit; extra == 'dev'
Requires-Dist: mypy; extra == 'dev'
Requires-Dist: pip-audit; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: pytest-cov; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material>=9.0; extra == 'docs'
Provides-Extra: extract
Requires-Dist: anthropic<1.0,>=0.40; extra == 'extract'
Provides-Extra: tui
Requires-Dist: textual<2.0,>=0.47; extra == 'tui'
Description-Content-Type: text/markdown

# wake

**A**gentic **U**nified **R**eduction of **A**gentic **L**ogs

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 Renderers** — `ProjectStatus` is rendered as CLAUDE.md (auto-read by the agent), `.wake/` directory files, or JSON. Renderers never touch the event store.

---

## Setup

### Install

```bash
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:

```bash
wake init --provider claude
```

For a Codex-only repo:

```bash
wake init --provider codex
```

For a mixed Claude + Codex repo:

```bash
wake init --provider both
```

Agent launchers:

```bash
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:

```json
{
  "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:

```markdown
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](dev/decisions/0002-projection-reducer-contract.md)):

- **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:

```python
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`:

```bash
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`:

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

```bash
# 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:

```bash
# 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:

```bash
pip install -e ".[docs]"
mkdocs serve
```

Or read the markdown directly in [`docs/`](docs/).

## Reference

- [CLI Reference](docs/reference/cli.md) — all commands, options, and environment variables
- [MCP Reference](docs/reference/mcp.md) — all MCP tools and resources with parameters

## ADRs

| ADR | Decision |
|-----|----------|
| [0001](dev/decisions/0001-event-store-backend.md) | SQLite WAL as primary backend (JSONL as fallback) |
| [0002](dev/decisions/0002-projection-reducer-contract.md) | Reducer purity, snapshot portability, bounded output |
| [0003](dev/decisions/0003-signal-classification.md) | Signal classification at ingestion time, not in reducer |
| [0004](dev/decisions/0004-explicit-empty-sections.md) | All sections always emitted, even when empty |
| [0005](dev/decisions/0005-cloud-session-persistence.md) | Commit rendered projections for cloud session continuity |

---

## License

Apache 2.0. See [LICENSE](LICENSE).
