# https://github.com/smaramwbc/statewave 项目说明书

生成时间：2026-05-15 10:02:16 UTC

## 目录

- [Overview - What is Statewave?](#page-overview)
- [Getting Started](#page-getting-started)
- [System Architecture](#page-architecture)
- [Database Schema & Migrations](#page-database-schema)
- [API Endpoints Reference](#page-api-endpoints)
- [Context Assembly & Ranking](#page-context-assembly)
- [Compilation Services](#page-compilation-services)
- [Embedding Services](#page-embeddings)
- [Governance - Receipts & Policy Engine](#page-receipts-policy)
- [Multi-tenancy & Security](#page-multi-tenancy)

<a id='page-overview'></a>

## Overview - What is Statewave?

### 相关页面

相关主题：[System Architecture](#page-architecture), [Getting Started](#page-getting-started)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/smaramwbc/statewave/blob/main/README.md)
- [SUPPORT.md](https://github.com/smaramwbc/statewave/blob/main/SUPPORT.md)
- [DOCKER.md](https://github.com/smaramwbc/statewave/blob/main/DOCKER.md)
- [CONTRIBUTING.md](https://github.com/smaramwbc/statewave/blob/main/CONTRIBUTING.md)
- [scripts/bootstrap_docs_pack.py](https://github.com/smaramwbc/statewave/blob/main/scripts/bootstrap_docs_pack.py)
- [scripts/docs_loader.py](https://github.com/smaramwbc/statewave/blob/main/scripts/docs_loader.py)
- [scripts/eval/eval_docs_support.py](https://github.com/smaramwbc/statewave/blob/main/scripts/eval/eval_docs_support.py)
- [server/starter_packs/statewave-support-agent/manifest.json](https://github.com/smaramwbc/statewave/blob/main/server/starter_packs/statewave-support-agent/manifest.json)
</details>

# Overview - What is Statewave?

Statewave is a self-hosted **memory infrastructure** for AI agents. It enables AI systems to store, retrieve, compile, and reason over long-term contextual information across conversations and sessions.

## Core Concept

Statewave solves the memory problem for AI agents by providing:

- **Episode Ingestion** — Capture events, conversations, and real-world signals as structured memory units
- **Memory Compilation** — Transform raw episodes into refined, actionable memories using LLM or heuristic approaches
- **Context Assembly** — Retrieve and rank relevant memories within token budgets for inference
- **Temporal Reasoning** — Track state changes and resolution status over time

The system operates as a REST API service that agents and applications query to build persistent, evolving memory without hand-writing custom storage and retrieval logic for each use case.

## Architecture Overview

```mermaid
graph TD
    subgraph "Sources"
        MCP[MCP Server]
        GH[GitHub]
        MD[Markdown Docs]
        SL[Slack]
        APP[Application]
    end

    subgraph "Statewave Core"
        INGEST[Ingestion API]
        COMPILE[Memory Compiler]
        SEARCH[Search API]
        CONTEXT[Context Assembly]
    end

    subgraph "Storage"
        PG[(PostgreSQL<br/>+ pgvector)]
    end

    MCP --> INGEST
    GH --> INGEST
    MD --> INGEST
    SL --> INGEST
    APP --> INGEST

    INGEST --> PG
    COMPILE --> PG
    SEARCH --> PG
    CONTEXT --> PG

    APP --> SEARCH
    APP --> CONTEXT
```

## Key Components

| Component | Description |
|-----------|-------------|
| **Episodes** | Raw, append-only event records captured from sources (conversations, issues, documents, etc.) |
| **Memories** | Compiled, refined knowledge derived from episodes through LLM or heuristic processing |
| **Subjects** | Logical namespaces grouping episodes and memories (typically one per user, customer, or agent persona) |
| **Resolutions** | Tracking of issue/session completion state per subject |
| **Context Bundle** | Ranked, token-bounded response assembled from relevant memories for inference |

## Data Flow

```mermaid
graph LR
    A[Source Event] --> B[POST /v1/episodes]
    B --> C[(Episode Store)]
    C --> D[POST /v1/memories/compile]
    D --> E[(Memory Store)]
    E --> F[GET /v1/memories/search]
    F --> G[POST /v1/context]
    G --> H[Ranked Context Bundle]
```

## API Reference

### Core Endpoints

| Method | Path | Purpose |
|--------|------|---------|
| `POST` | `/v1/episodes` | Ingest a single episode (append-only) |
| `POST` | `/v1/episodes/batch` | Ingest up to 100 episodes at once |
| `POST` | `/v1/memories/compile` | Compile memories from episodes (idempotent) |
| `GET` | `/v1/memories/search` | Search by kind, text, or semantic similarity |
| `POST` | `/v1/context` | Assemble ranked, token-bounded context bundle |

### Subject Management

| Method | Path | Purpose |
|--------|------|---------|
| `GET` | `/v1/timeline` | Chronological subject timeline |
| `GET` | `/v1/subjects` | List known subjects with episode/memory counts |
| `DELETE` | `/v1/subjects/{id}` | Permanently delete all data for a subject |
| `GET` | `/v1/subjects/{id}/health` | Customer health score with explainable factors |
| `GET` | `/v1/subjects/{id}/sla` | SLA metrics — response time, resolution time, breaches |

### Resolution Tracking

| Method | Path | Purpose |
|--------|------|---------|
| `POST` | `/v1/resolutions` | Track issue resolution state per session |
| `GET` | `/v1/resolutions` | List resolutions for a subject |

### Additional Endpoints

| Method | Path | Purpose |
|--------|------|---------|
| `POST` | `/v1/handoff` | Generate compact handoff context pack |
| `GET` | `/healthz` or `/health` | Liveness check |
| `GET` | `/readyz` or `/ready` | Readiness check |

资料来源：[README.md:26-49](https://github.com/smaramwbc/statewave/blob/main/README.md)

## Connectors

Statewave is not limited to live chat transcripts. **Connectors** feed real-world events into Statewave as episodes, enabling agents to build memory from diverse sources.

| Source | Memory Shape | Status |
|--------|--------------|--------|
| MCP server | Copilot / Claude / Cursor / agent memory | ✅ Shipped |
| GitHub | Issues, PRs, reviews, releases → repo memory | ✅ Shipped |
| Markdown | Local docs, ADRs, RFCs → decision memory | ✅ Shipped |
| Slack | Channel messages → conversation memory | ✅ Shipped |

资料来源：[README.md:53-60](https://github.com/smaramwbc/statewave/blob/main/README.md)

## Deployment

### Docker Deployment

Statewave is deployed as a containerized service using Docker Compose:

```yaml
services:
  app:
    image: statewavedev/statewave:latest
    ports: ["8100:8100"]
    environment:
      STATEWAVE_DATABASE_URL: postgresql+asyncpg://statewave:statewave@db:5432/statewave
      STATEWAVE_LITELLM_API_KEY: sk-...
    depends_on:
      db:
        condition: service_healthy
```

```bash
docker compose up -d
curl http://localhost:8100/healthz
```

资料来源：[DOCKER.md](https://github.com/smaramwbc/statewave/blob/main/DOCKER.md)

### Environment Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `STATEWAVE_DATABASE_URL` | — | PostgreSQL connection string (required) |
| `STATEWAVE_LITELLM_API_KEY` | — | LiteLLM API key for LLM compilation |
| `STATEWAVE_LOG_LEVEL` | `INFO` | Logging verbosity |
| `STATEWAVE_MAX_CONTEXT_TOKENS` | `4000` | Default token budget for context assembly |
| `STATEWAVE_CORS_ORIGINS` | `["*"]` | Allowed CORS origins |
| `STATEWAVE_RATE_LIMIT` | `0` | Requests/min/IP (0 = disabled) |
| `STATEWAVE_WEBHOOK_URL` | — | Webhook callback URL |
| `STATEWAVE_TENANT_HEADER` | `X-Tenant-ID` | Header for multi-tenant isolation |

资料来源：[README.md:93-107](https://github.com/smaramwbc/statewave/blob/main/README.md)

## Multi-Tenant Support

Statewave supports multi-tenant deployments through application-layer isolation:

- **Tenant Header**: `X-Tenant-ID` header identifies the tenant (configurable via `STATEWAVE_TENANT_HEADER`)
- **Query-Scoped Isolation**: Data isolation is enforced at the query layer
- **Per-Tenant Configuration**: Policy bundles and receipts support tenant-specific settings

资料来源：[README.md:100-107](https://github.com/smaramwbc/statewave/blob/main/README.md)

## Documentation System

Statewave includes a documentation-grounded memory system:

- Curated documentation sections are chunked at heading boundaries
- Each section becomes an episode with `source="statewave-docs"`, `type="doc_section"`
- Episodes carry a deterministic content hash for change detection
- A bundled starter pack provides 276 episodes and 364 compiled memories out of the box

资料来源：[scripts/docs_loader.py](https://github.com/smaramwbc/statewave/blob/main/scripts/docs_loader.py)
资料来源：[server/starter_packs/statewave-support-agent/manifest.json](https://github.com/smaramwbc/statewave/blob/main/server/starter_packs/statewave-support-agent/manifest.json)

## Evaluation Framework

The repository includes an evaluation framework for testing docs-grounded retrieval:

```python
@dataclass
class Question:
    task: str
    expected_doc_paths: list[str]
    expected_terms: list[str]
```

Sample evaluation questions verify that the system correctly surfaces documentation for queries about deployment, compilation modes, hardware requirements, and API contracts.

资料来源：[scripts/eval/eval_docs_support.py](https://github.com/smaramwbc/statewave/blob/main/scripts/eval/eval_docs_support.py)

## Starter Packs

Statewave ships with pre-built starter packs:

| Pack | Description |
|------|-------------|
| `statewave-support-agent` | Full Statewave Support docs pack with 276 episodes and 364 memories |
| `demo-support-agent` | Support agent persona |
| `demo-coding-assistant` | Coding assistant persona |
| `demo-sales-copilot` | Sales copilot persona |
| `demo-devops-agent` | DevOps agent persona |
| `demo-research-assistant` | Research assistant persona |

资料来源：[server/starter_packs/statewave-support-agent/manifest.json](https://github.com/smaramwbc/statewave/blob/main/server/starter_packs/statewave-support-agent/manifest.json)
资料来源：[scripts/build_demo_packs.py](https://github.com/smaramwbc/statewave/blob/main/scripts/build_demo_packs.py)

## Health Monitoring

Statewave provides subject-level health monitoring:

- **Health Score**: Aggregated customer health with explainable factors
- **SLA Metrics**: Response time, resolution time, and breach tracking
- **Snapshot Restore**: Restore subject state from snapshots with timestamp shifting

资料来源：[README.md:46-47](https://github.com/smaramwbc/statewave/blob/main/README.md)
资料来源：[server/services/snapshots.py](https://github.com/smaramwbc/statewave/blob/main/server/services/snapshots.py)

## Getting Started

1. **Quick Start**:
   ```bash
   docker compose up db -d
   python -m venv .venv && source .venv/bin/activate
   pip install -e ".[dev,llm]"
   alembic upgrade head
   uvicorn server.main:app --host 0.0.0.0 --port 8100
   ```

2. **Run Tests**:
   ```bash
   # Unit tests (no DB required)
   pytest tests/test_*.py -v

   # Integration tests (requires Postgres)
   PGPASSWORD=statewave createdb -h localhost -U statewave statewave_test
   pytest tests/integration/ -v
   ```

资料来源：[CONTRIBUTING.md](https://github.com/smaramwbc/statewave/blob/main/CONTRIBUTING.md)
资料来源：[README.md:11-19](https://github.com/smaramwbc/statewave/blob/main/README.md)

## Current Limitations

| Limitation | Description |
|------------|-------------|
| Rate limiting per-IP | Distributed (Postgres-backed), but keyed by IP only, not per-tenant or per-API-key |
| Multi-tenant app-layer | Query-scoped data isolation + per-tenant config, but no Postgres RLS |

资料来源：[README.md:117-122](https://github.com/smaramwbc/statewave/blob/main/README.md)

## Repository Structure

| Repository | Purpose |
|------------|---------|
| [statewave](https://github.com/smaramwbc/statewave) | Backend API, server, core |
| [statewave-py](https://github.com/smaramwbc/statewave-py) | Python SDK (`pip install statewave`) |
| [statewave-ts](https://github.com/smaramwbc/statewave-ts) | TypeScript SDK (`npm install @statewavedev/sdk`) |
| [statewave-connectors](https://github.com/smaramwbc/statewave-connectors) | Connectors (GitHub, Markdown, MCP, etc.) |
| [statewave-docs](https://github.com/smaramwbc/statewave-docs) | Documentation |
| [statewave-examples](https://github.com/smaramwbc/statewave-examples) | Working code examples |
| [statewave-web](https://github.com/smaramwbc/statewave-web) | Marketing site + embedded demo |
| [statewave-admin](https://github.com/smaramwbc/statewave-admin) | Admin dashboard |

资料来源：[SUPPORT.md:73-83](https://github.com/smaramwbc/statewave/blob/main/SUPPORT.md)

---

<a id='page-getting-started'></a>

## Getting Started

### 相关页面

相关主题：[Overview - What is Statewave?](#page-overview), [API Endpoints Reference](#page-api-endpoints)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/smaramwbc/statewave/blob/main/README.md)
- [DOCKER.md](https://github.com/smaramwbc/statewave/blob/main/DOCKER.md)
- [helm/statewave/README.md](https://github.com/smaramwbc/statewave/blob/main/helm/statewave/README.md)
- [scripts/bootstrap_docs_pack.py](https://github.com/smaramwbc/statewave/blob/main/scripts/bootstrap_docs_pack.py)
- [scripts/seed_demo_subjects.py](https://github.com/smaramwbc/statewave/blob/main/scripts/seed_demo_subjects.py)
- [server/services/context.py](https://github.com/smaramwbc/statewave/blob/main/server/services/context.py)
</details>

# Getting Started

## Overview

The Getting Started guide provides developers and operators with the essential knowledge to install, configure, and run Statewave — an agent memory system that captures, compiles, and retrieves episodic and semantic memory for AI agents. The guide covers three primary deployment paths: Docker Compose for local development, Helm charts for Kubernetes production environments, and Python packages for programmatic integration.

Statewave operates as an append-only memory store where **episodes** represent raw events (chat messages, commits, support tickets) and **memories** are compiled, high-confidence knowledge units derived from episodes through LLM or heuristic processing. The system enables agents to assemble token-bounded context bundles for RAG (Retrieval-Augmented Generation) workflows.

资料来源：[README.md:1-50]()

## System Architecture

```mermaid
graph TD
    subgraph "Ingestion Layer"
        E[Episodes] --> B[Batch Ingest]
        E --> S[Single Ingest]
    end
    
    subgraph "Compilation Layer"
        S --> COMP[Memory Compiler]
        B --> COMP
        COMP -->|LLM or Heuristic| M[Memories]
    end
    
    subgraph "Retrieval Layer"
        M --> CX[Context Assembler]
        Q[Query] --> CX
        CX --> CB[Token-Bounded Context]
    end
    
    subgraph "Storage Layer"
        M --> PG[(PostgreSQL)]
        E --> PG
    end
    
    subgraph "Connectors"
        MCP[MCP Server]
        GH[GitHub]
        MD[Markdown]
        SL[Slack]
        MCP --> E
        GH --> E
        MD --> E
        SL --> E
    end
```

## Prerequisites

Before beginning the installation, ensure the following requirements are met:

| Requirement | Minimum Version | Purpose |
|-------------|-----------------|---------|
| Python | 3.11+ | SDK and CLI usage |
| Docker | 24.0+ | Container runtime |
| Docker Compose | 2.20+ | Multi-container orchestration |
| PostgreSQL | 15+ | Primary data store |
| Kubernetes | 1.28+ | Production deployment |
| Helm | 3.14+ | Chart package manager |

资料来源：[DOCKER.md:1-30]()

## Installation Methods

### Quick Start with Docker Compose

The fastest path to a running Statewave instance uses Docker Compose:

```yaml
# docker-compose.yml
services:
  api:
    image: statewavedev/statewave:latest
    ports: ["8100:8100"]
    environment:
      STATEWAVE_DATABASE_URL: postgresql+asyncpg://statewave:statewave@db:5432/statewave
      STATEWAVE_LITELLM_API_KEY: sk-...
    depends_on:
      db:
        condition: service_healthy

volumes:
  pgdata:
```

资料来源：[DOCKER.md:40-55]()

Execute the following commands to start Statewave:

```bash
# Start the stack
docker compose up -d

# Verify health
curl http://localhost:8100/healthz
```

### Pinning a Specific Version

Production deployments should pin to a specific version:

```bash
STATEWAVE_VERSION=0.7.0 docker compose up -d
```

Available tagging conventions:

| Tag Pattern | Meaning |
|-------------|---------|
| `latest` | Tip of `main` branch |
| `X.Y.Z` | Specific semver release |
| `X.Y` | Latest in the minor line |
| `X` | Latest in the major line |
| `sha-<7>` | Specific git commit |

资料来源：[DOCKER.md:70-85]()

### Verify Build Attestation

For supply chain security, verify the container image:

```bash
gh attestation verify \
  oci://docker.io/statewavedev/statewave:latest \
  --owner smaramwbc
```

资料来源：[DOCKER.md:90-95]()

## Environment Configuration

Statewave uses environment variables for all configuration. The following table documents the complete set:

| Variable | Default | Description |
|----------|---------|-------------|
| `STATEWAVE_DATABASE_URL` | — | PostgreSQL connection string (required) |
| `STATEWAVE_LITELLM_API_KEY` | — | LLM provider API key |
| `STATEWAVE_EMBEDDING_PROVIDER` | `litellm` | Embedding backend (`litellm`, `openai`, `stub`) |
| `STATEWAVE_COMPILER_TYPE` | `llm` | Compilation method (`llm` or `heuristic`) |
| `STATEWAVE_AUTH_API_KEY` | — | Static API key for authentication |
| `STATEWAVE_PORT` | `8100` | HTTP server port |
| `STATEWAVE_LOG_LEVEL` | `INFO` | Logging verbosity |
| `STATEWAVE_RATE_LIMIT` | `0` | Requests per minute per IP |
| `STATEWAVE_RATE_LIMIT_STRATEGY` | `distributed` | `distributed` (Postgres) or `memory` |
| `STATEWAVE_WEBHOOK_URL` | — | Callback URL for async events |
| `STATEWAVE_WEBHOOK_TIMEOUT` | `5.0` | Webhook HTTP timeout (seconds) |
| `STATEWAVE_TENANT_HEADER` | `X-Tenant-ID` | Multi-tenant isolation header |
| `STATEWAVE_REQUIRE_TENANT` | `false` | Enforce tenant header presence |
| `STATEWAVE_DEFAULT_MAX_CONTEXT_TOKENS` | `4000` | Token budget for context assembly |
| `STATEWAVE_CORS_ORIGINS` | `["*"]` | Allowed CORS origins |

资料来源：[README.md:180-200]()

## API Endpoints

### Health and Readiness

| Endpoint | Method | Purpose |
|----------|--------|---------|
| `/healthz` | GET | Liveness check |
| `/health` | GET | Liveness check (alias) |
| `/readyz` | GET | Readiness check |
| `/ready` | GET | Readiness check (alias) |

### Core Memory Operations

| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/v1/episodes` | Ingest a single episode (append-only) |
| `POST` | `/v1/episodes/batch` | Ingest up to 100 episodes at once |
| `POST` | `/v1/memories/compile` | Compile memories from episodes (idempotent) |
| `GET` | `/v1/memories/search` | Search by kind, text, or semantic similarity |
| `POST` | `/v1/context` | Assemble ranked, token-bounded context bundle |
| `GET` | `/v1/timeline` | Chronological subject timeline |
| `GET` | `/v1/subjects` | List known subjects with episode/memory counts |
| `DELETE` | `/v1/subjects/{id}` | Permanently delete all data for a subject |

### Advanced Operations

| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/v1/resolutions` | Track issue resolution state per session |
| `GET` | `/v1/resolutions` | List resolutions for a subject |
| `POST` | `/v1/handoff` | Generate compact handoff context pack |
| `GET` | `/v1/subjects/{id}/health` | Customer health score with explainable factors |
| `GET` | `/v1/subjects/{id}/sla` | SLA metrics — response time, resolution time, breaches |

资料来源：[README.md:30-60]()

## Connectors Ecosystem

Statewave supports external data sources through modular connectors:

```bash
# Install connectors individually
npm install @statewavedev/connectors-github
npm install @statewavedev/connectors-markdown
npm install @statewavedev/connectors-slack
npm install @statewavedev/connectors-n8n
npm install @statewavedev/connectors-zapier
npm install @statewavedev/mcp-server

# Meta-package for all connectors
npm install @statewavedev/connectors
```

Available connectors and their status:

| Source | Memory Shape | Status |
|--------|--------------|--------|
| MCP Server | Copilot/Claude/Cursor/agent memory | ✅ Shipped |
| GitHub | Issues, PRs, reviews, releases → repo memory | ✅ Shipped |
| Markdown | Local docs, ADRs, RFCs → decision memory | ✅ Shipped |
| Slack | Channel conversations | ✅ Shipped |

资料来源：[README.md:250-280]()

### Connector Usage Examples

```bash
# Sync GitHub data (dry-run first)
statewave-connectors sync github \
  --repo smaramwbc/statewave \
  --subject repo:smaramwbc/statewave \
  --dry-run

# Sync Markdown documentation
statewave-connectors sync markdown \
  --path ./docs \
  --subject repo:smaramwbc/statewave \
  --dry-run

# Start MCP server
statewave-connectors mcp start
```

## Kubernetes Deployment

For production environments, use the Helm chart:

```bash
helm install statewave ./helm/statewave \
  --set database.url='postgresql+asyncpg://...' \
  --set llm.apiKey='sk-...' \
  --set auth.apiKey='replace-me'
```

The Helm chart runs a pre-install Job (`alembic upgrade head`) before any API pod admits traffic. Upgrades repeat migration as a pre-upgrade Job.

资料来源：[helm/statewave/README.md:1-40]()

### Helm Configuration Reference

| Value | Default | Notes |
|-------|---------|-------|
| `image.tag` | `""` | Falls back to `Chart.AppVersion` |
| `replicaCount` | `1` | Check connection-budget math before raising |
| `database.url` / `database.existingSecret` | — | One is **required** |
| `compiler.type` | `llm` | `heuristic` for demo/no-LLM mode |
| `embedding.provider` | `litellm` | `stub` for demo/no-embedding mode |
| `llm.apiKey` / `llm.existingSecret` | — | Required when `compiler.type=llm` |
| `auth.apiKey` / `auth.existingSecret` | — | Strongly recommended in production |

资料来源：[helm/statewave/README.md:50-70]()

## Running Tests

Statewave includes unit tests (no database required) and integration tests (requires PostgreSQL):

```bash
# Unit tests
pytest tests/test_*.py -v

# Integration tests
PGPASSWORD=statewave createdb -h localhost -U statewave statewave_test
pytest tests/integration/ -v

# All tests
pytest tests/ -v
```

资料来源：[README.md:210-220]()

## Bootstrap Documentation Pack

After initial deployment, bootstrap the documentation memory pack for a self-hosted support agent:

```bash
python -m scripts.bootstrap_docs_pack [--docs-path PATH] [--purge] [--dry-run]
```

### Environment Variables for Bootstrap

| Variable | Default | Purpose |
|----------|---------|---------|
| `STATEWAVE_URL` | `http://localhost:8100` | API base URL |
| `STATEWAVE_API_KEY` | — | Authentication key |
| `STATEWAVE_DOCS_PATH` | — | Override documentation path |

### Bootstrap Behavior

```mermaid
graph LR
    A[Read statewave-docs corpus] --> B[Ingest each section as Episode]
    B --> C[Compile Episodes to Memories]
    C --> D[Assemble docs-grounded Knowledge Base]
    D --> E[Support Agent queries via POST /v1/context]
    
    F[--purge] -.->|Wipe and rebuild| A
    G[--dry-run] -.->|No ingestion| A
```

The bootstrap process is idempotent. By default, it fails if the subject already has episodes. Re-run with `--purge` to wipe and rebuild from scratch.

资料来源：[scripts/bootstrap_docs_pack.py:1-50]()

## Seed Demo Subjects

For development and testing, seed demo subjects with sample data:

```bash
python -m scripts.seed_demo_subjects [--dry-run]
```

This creates sample episodes and memories that reference each other, enabling verification of the compilation and retrieval pipeline.

资料来源：[scripts/seed_demo_subjects.py:1-60]()

## Current Limitations

Statewave v0.8.0 has known limitations:

| Limitation | Description |
|------------|-------------|
| Rate limiting | Per-IP only — not yet per-tenant or per-API-key |
| Multi-tenant isolation | App-layer query-scoped isolation — no Postgres RLS yet |

资料来源：[README.md:225-235]()

## Next Steps

| Resource | Purpose |
|----------|---------|
| [API v1 contract](https://github.com/smaramwbc/statewave-docs/blob/main/api/v1-contract.md) | Complete API reference |
| [Connector docs](https://github.com/smaramwbc/statewave-docs/blob/main/connectors/index.md) | Connector configuration |
| [Horizontal Scaling Guide](https://github.com/smaramwbc/statewave-docs/blob/main/deployment/horizontal-scaling.md) | Production scaling guidance |
| [Architecture documentation](https://github.com/smaramwbc/statewave-docs/blob/main/architecture/ranking.md) | Context ranking internals |

---

<a id='page-architecture'></a>

## System Architecture

### 相关页面

相关主题：[Overview - What is Statewave?](#page-overview), [Database Schema & Migrations](#page-database-schema), [Compilation Services](#page-compilation-services)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/smaramwbc/statewave/blob/main/README.md)
- [server/db/tables.py](https://github.com/smaramwbc/statewave/blob/main/server/db/tables.py)
- [server/api/admin.py](https://github.com/smaramwbc/statewave/blob/main/server/api/admin.py)
- [server/api/timeline.py](https://github.com/smaramwbc/statewave/blob/main/server/api/timeline.py)
- [server/api/handoff.py](https://github.com/smaramwbc/statewave/blob/main/server/api/handoff.py)
- [server/services/snapshots.py](https://github.com/smaramwbc/statewave/blob/main/server/services/snapshots.py)
- [server/services/compilers/heuristic.py](https://github.com/smaramwbc/statewave/blob/main/server/services/compilers/heuristic.py)
- [DOCKER.md](https://github.com/smaramwbc/statewave/blob/main/DOCKER.md)
</details>

# System Architecture

Statewave is an AI agent memory system designed to ingest real-world events as episodes, compile them into structured memories, and retrieve ranked, token-bounded context for agentic workflows. The system follows a layered architecture with clear separation between API handling, business logic services, data persistence, and external connectors.

## High-Level Architecture Overview

```mermaid
graph TD
    subgraph "External Clients"
        A[Agents / Applications]
        B[Connectors: GitHub, Slack, MCP]
        C[SDK Clients]
    end

    subgraph "API Layer"
        D[FastAPI Application]
        E[Endpoint Routes]
        F[Middleware / Auth]
    end

    subgraph "Service Layer"
        G[Episode Service]
        H[Memory Service]
        I[Compiler Service]
        J[Search Service]
        K[Snapshot Service]
    end

    subgraph "Data Layer"
        L[(PostgreSQL)]
        M[(Vector Embeddings)]
        N[Starter Packs]
    end

    A --> D
    B --> D
    C --> D
    D --> F
    F --> E
    E --> G
    E --> H
    E --> I
    E --> J
    E --> K
    G --> L
    H --> L
    H --> M
    I --> L
    J --> M
    K --> L
    K --> N
```

## Core Data Models

### Episode

An **Episode** represents a discrete unit of experience — a chat message, a support ticket update, a GitHub issue event, or a documentation section. Episodes are the atomic ingestion unit in Statewave.

| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | Unique identifier |
| `subject_id` | UUID | Parent subject this episode belongs to |
| `session_id` | str | Session context identifier |
| `type` | str | Kind of episode (e.g., `doc_section`, `chat`, `ticket`) |
| `source` | str | Origin system (e.g., `github`, `slack`, `statewave-docs`) |
| `content` | str | Raw content body |
| `metadata_` | dict | Arbitrary key-value metadata |
| `provenance` | dict | Source-specific tracking (e.g., `content_hash` for docs) |
| `created_at` | datetime | Ingestion timestamp |
| `valid_from` | datetime | Temporal anchor for memory derivation |

资料来源：[server/db/tables.py](https://github.com/smaramwbc/statewave/blob/main/server/db/tables.py)

### Memory

A **Memory** is a compiled, structured artifact extracted from one or more episodes. Memories have temporal validity windows, confidence scores, and lineage tracking.

| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | Unique identifier |
| `subject_id` | UUID | Parent subject |
| `kind` | str | Memory category (e.g., `episode_summary`, `profile_fact`, `decision`) |
| `content` | str | Compiled memory content |
| `summary` | str | Short summary (max 200 chars) |
| `confidence` | float | Confidence score (0.0-1.0) |
| `valid_from` | datetime | Start of validity window |
| `valid_to` | datetime | End of validity window (nullable) |
| `source_episode_ids` | list[UUID] | Episodes this memory was derived from |
| `status` | str | Memory lifecycle state |
| `sensitivity_labels` | list[str] | Optional sensitivity classification |

资料来源：[server/api/timeline.py](https://github.com/smaramwbc/statewave/blob/main/server/api/timeline.py)

### Subject

A **Subject** is the central entity in Statewave — a persona, entity, or concept that accumulates memory over time.

| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | Unique identifier |
| `tenant_id` | str | Multi-tenant isolation key |
| `name` | str | Human-readable name |
| `type_` | str | Subject type classification |
| `metadata_` | dict | Arbitrary metadata |
| `embedding` | vector | Semantic embedding for similarity search |

资料来源：[server/db/tables.py](https://github.com/smaramwbc/statewave/blob/main/server/db/tables.py)

## API Layer

### REST API v1

The API is served by FastAPI on port 8100 by default and exposes OpenAPI documentation at `/docs` and ReDoc at `/redoc`.

```mermaid
graph LR
    A[Client] --> B["POST /v1/episodes"]
    A --> C["POST /v1/episodes/batch"]
    A --> D["POST /v1/memories/compile"]
    A --> E["GET /v1/memories/search"]
    A --> F["POST /v1/context"]
    A --> G["GET /v1/timeline"]
    A --> H["GET /v1/subjects"]
    A --> I["DELETE /v1/subjects/{id}"]
    A --> J["POST /v1/resolutions"]
    A --> K["POST /v1/handoff"]
    A --> L["GET /v1/subjects/{id}/health"]
    A --> M["GET /v1/subjects/{id}/sla"]
```

### Core Endpoints

| Method | Path | Purpose |
|--------|------|---------|
| `POST` | `/v1/episodes` | Ingest a single episode (append-only) |
| `POST` | `/v1/episodes/batch` | Ingest up to 100 episodes at once |
| `POST` | `/v1/memories/compile` | Compile memories from episodes (idempotent) |
| `GET` | `/v1/memories/search` | Search by kind, text, or semantic similarity |
| `POST` | `/v1/context` | Assemble ranked, token-bounded context bundle |
| `GET` | `/v1/timeline` | Chronological subject timeline |
| `GET` | `/v1/subjects` | List known subjects with episode/memory counts |
| `DELETE` | `/v1/subjects/{id}` | Permanently delete all data for a subject |
| `POST` | `/v1/resolutions` | Track issue resolution state per session |
| `GET` | `/v1/resolutions` | List resolutions for a subject |
| `POST` | `/v1/handoff` | Generate compact handoff context pack |

资料来源：[README.md](https://github.com/smaramwbc/statewave/blob/main/README.md)

### Health Endpoints

| Endpoint | Purpose |
|----------|---------|
| `GET /healthz` or `GET /health` | Liveness check |
| `GET /readyz` or `GET /ready` | Readiness check |

资料来源：[README.md](https://github.com/smaramwbc/statewave/blob/main/README.md)

## Service Layer

### Compiler Service

The compiler extracts memories from episodes. Statewave supports two compilation modes:

| Mode | Description | Use Case |
|------|-------------|----------|
| `llm` | Uses an LLM provider (via LiteLLM) to extract structured memories | Production with semantic understanding |
| `heuristic` | Rule-based extraction for basic facts and summaries | Demo mode, no API key required |

#### Heuristic Compiler

The heuristic compiler extracts two memory kinds from episodes:

1. **episode_summary**: A 500-character truncation of the episode text with 0.8 confidence
2. **profile_fact**: Extracted profile facts with 0.6 confidence

```python
MemoryRow(
    kind="episode_summary",
    content=text[:500],
    summary=text[:200],
    confidence=0.8,
    valid_from=ep_valid_from,
    valid_to=compute_valid_to("episode_summary", ep_valid_from, ttl),
    source_episode_ids=[ep.id],
    status="active",
)
```

资料来源：[server/services/compilers/heuristic.py](https://github.com/smaramwbc/statewave/blob/main/server/services/compilers/heuristic.py)

#### Temporal Anchoring

The compiler determines episode temporal anchors with the following priority:

1. `payload.event_time` — Explicit client override for historical data replay
2. `payload.messages[0].timestamp` — First message timestamp in chat-shaped payloads (used by LoCoMo, Slack, Zendesk connectors)
3. `created_at` — Fallback to ingestion timestamp

```mermaid
graph TD
    A[Episode Ingestion] --> B{event_time present?}
    B -->|Yes| C[Use event_time]
    B -->|No| D{messages timestamp present?}
    D -->|Yes| E[Use messages[0].timestamp]
    D -->|No| F[Use created_at]
```

资料来源：[server/services/compilers/heuristic.py](https://github.com/smaramwbc/statewave/blob/main/server/services/compilers/heuristic.py)

### Snapshot Service

Snapshots enable point-in-time restoration of subject data, including episodes, memories, and resolutions.

| Feature | Description |
|---------|-------------|
| Create Snapshot | Captures all subject data with version metadata |
| Restore Snapshot | Clones data with optional time-shifting for temporal consistency |
| Metadata Tracking | Records `restored_from_snapshot` in restored records |

```python
snapshot = SubjectSnapshotRow(
    name=name,
    version=version,
    source_subject_id=snapshot_subject,
    episode_count=len(eps),
    memory_count=len(mems),
    metadata_=metadata or {},
)
```

资料来源：[server/services/snapshots.py](https://github.com/smaramwbc/statewave/blob/main/server/services/snapshots.py)

### Timeline Service

The timeline service provides chronological event merging across episodes and resolutions within a session.

```mermaid
graph TD
    A[Session Request] --> B[Fetch Episodes]
    A --> C[Fetch Resolutions]
    B --> D[Merge Events Chronologically]
    C --> D
    D --> E[TimelineResponse]
    
    E --> F[TimelineEpisodeEvent]
    E --> G[TimelineResolutionEvent]
    
    F --> H[id]
    F --> I[source]
    F --> J[type]
    F --> K[payload]
    F --> L[created_at]
    F --> M[citing_memory_count]
    
    G --> N[resolved_at]
    G --> O[status]
```

#### Timeline Response Model

| Field | Type | Description |
|-------|------|-------------|
| `session_id` | str | Session identifier |
| `status` | str | Overall session status |
| `first_message_at` | str | Timestamp of first user message |
| `first_response_at` | str | Timestamp of first agent response |
| `resolved_at` | str | Resolution timestamp |
| `first_response_seconds` | float | Time to first response |
| `resolution_seconds` | float | Total resolution time |
| `first_response_breached` | bool | SLA breach indicator |
| `resolution_breached` | bool | Resolution SLA breach indicator |
| `episode_count` | int | Total episodes in session |
| `events` | list | Chronologically merged events |

资料来源：[server/api/admin.py](https://github.com/smaramwbc/statewave/blob/main/server/api/admin.py)

### Handoff Service

The handoff endpoint generates compact context packs for transferring conversation context between agents or sessions.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `subject_id` | UUID | Yes | Target subject |
| `session_id` | str | No | Optional session context |
| `reason` | str | No | Handoff reason |
| `max_tokens` | int | No | Token budget for context |
| `caller_id` | str | Conditional | Required when tenant config mandates it |
| `caller_type` | str | Conditional | Required when tenant config mandates it |

资料来源：[server/api/handoff.py](https://github.com/smaramwbc/statewave/blob/main/server/api/handoff.py)

## Data Layer

### Database Schema

Statewave uses PostgreSQL with SQLAlchemy async for data persistence. Key tables:

| Table | Purpose |
|-------|---------|
| `subjects` | Central entity registry with embeddings |
| `episodes` | Raw experience ingestion records |
| `memories` | Compiled memory artifacts with validity windows |
| `resolutions` | Issue resolution tracking per session |
| `policy_bundles` | Immutable policy YAML bundles |
| `policy_receipts` | Compliance receipts for policy enforcement |
| `subject_snapshots` | Point-in-time backup metadata |

### Multi-Tenancy

Multi-tenant isolation is implemented at the application layer:

| Configuration | Default | Description |
|---------------|--------|-------------|
| `STATEWAVE_TENANT_HEADER` | `X-Tenant-ID` | Header for multi-tenant isolation |
| `STATEWAVE_REQUIRE_TENANT` | `false` | Reject requests without tenant header |

资料来源：[README.md](https://github.com/smaramwbc/statewave/blob/main/README.md)

> **Current Limitation**: Multi-tenant isolation is query-scoped (application-layer) with no PostgreSQL Row-Level Security (RLS) enforcement.

## Connectors

Connectors feed external data sources into Statewave as episodes without requiring custom ingestion code.

```mermaid
graph LR
    A[GitHub] -->|Issues, PRs, Reviews| B[Connector]
    C[Slack] -->|Messages, Threads| B
    D[MCP Server] -->|Agent Memory| B
    E[Markdown] -->|Docs, RFCs| B
    F[n8n] -->|Workflow Events| B
    G[Zapier] -->|Automation Triggers| B
    
    B --> H[Episodes]
    H --> I[Statewave API]
```

### Available Connectors

| Source | Memory Shape | Status |
|--------|--------------|--------|
| MCP Server | Copilot / Claude / Cursor / agent memory | ✅ Shipped |
| GitHub | Issues, PRs, reviews, releases → repo memory | ✅ Shipped |
| Markdown | Local docs, ADRs, RFCs → decision memory | ✅ Shipped |
| Slack | Channel messages → conversation memory | ✅ Shipped |
| n8n | Workflow events → process memory | ✅ Shipped |
| Zapier | Automation triggers → event memory | ✅ Shipped |

资料来源：[README.md](https://github.com/smaramwbc/statewave/blob/main/README.md)

## Configuration

### Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `STATEWAVE_DATABASE_URL` | — | PostgreSQL connection string (required) |
| `STATEWAVE_LITELLM_API_KEY` | — | LiteLLM API key for LLM compilation |
| `STATEWAVE_EMBEDDING_PROVIDER` | `litellm` | Embedding provider |
| `STATEWAVE_COMPILER_TYPE` | `llm` | Compiler mode: `llm` or `heuristic` |
| `STATEWAVE_API_KEY` | — | API authentication key |
| `STATEWAVE_PORT` | `8100` | HTTP server port |
| `STATEWAVE_RATE_LIMIT` | `0` | Requests/min/IP (0 = disabled) |
| `STATEWAVE_RATE_LIMIT_STRATEGY` | `distributed` | `distributed` (Postgres) or `memory` |
| `STATEWAVE_WEBHOOK_URL` | — | Webhook callback URL |
| `STATEWAVE_WEBHOOK_TIMEOUT` | `5.0` | Webhook HTTP timeout (seconds) |
| `STATEWAVE_TENANT_HEADER` | `X-Tenant-ID` | Tenant isolation header |
| `STATEWAVE_REQUIRE_TENANT` | `false` | Reject requests without tenant |
| `STATEWAVE_DEFAULT_MAX_CONTEXT_TOKENS` | `4000` | Default token budget for context |
| `STATEWAVE_CORS_ORIGINS` | `["*"]` | Allowed CORS origins |

资料来源：[README.md](https://github.com/smaramwbc/statewave/blob/main/README.md)

## Deployment

### Docker Deployment

Statewave is distributed as a Docker image and can be deployed using Docker Compose.

```yaml
services:
  statewave:
    image: statewavedev/statewave:latest
    ports: ["8100:8100"]
    environment:
      STATEWAVE_DATABASE_URL: postgresql+asyncpg://statewave:statewave@db:5432/statewave
      STATEWAVE_LITELLM_API_KEY: sk-...
    depends_on:
      db:
        condition: service_healthy
```

#### Image Tags

| Tag | Meaning |
|-----|---------|
| `latest` | Tip of `main` |
| `X.Y.Z` | Semver release |
| `X.Y` | Latest in the minor line |
| `X` | Latest in the major line |
| `sha-<7>` | Specific commit |

资料来源：[DOCKER.md](https://github.com/smaramwbc/statewave/blob/main/DOCKER.md)

### Helm Chart

Kubernetes deployment is supported via Helm:

| Value | Default | Notes |
|-------|---------|-------|
| `image.tag` | `""` | Falls back to Chart.AppVersion |
| `replicaCount` | `1` | See horizontal scaling guide |
| `database.url` | — | Required |
| `database.existingSecret` | — | Alternative to url |
| `compiler.type` | `llm` | `heuristic` for demo mode |
| `embedding.provider` | `litellm` | `stub` for no-embedding mode |
| `llm.apiKey` | — | Required for LLM mode |
| `auth.apiKey` | — | Recommended for production |

资料来源：[helm/statewave/README.md](https://github.com/smaramwbc/statewave/blob/main/helm/statewave/README.md)

## Testing

### Test Execution

```bash
# Unit tests (no DB required)
pytest tests/test_*.py -v

# Integration tests (requires Postgres)
PGPASSWORD=statewave createdb -h localhost -U statewave statewave_test
pytest tests/integration/ -v

# All tests
pytest tests/ -v
```

资料来源：[README.md](https://github.com/smaramwbc/statewave/blob/main/README.md)

---

<a id='page-database-schema'></a>

## Database Schema & Migrations

### 相关页面

相关主题：[System Architecture](#page-architecture), [Governance - Receipts & Policy Engine](#page-receipts-policy)

<details>
<summary>Relevant Source Files</summary>

以下源码文件用于生成本页说明：

- [server/db/tables.py](https://github.com/smaramwbc/statewave/blob/main/server/db/tables.py)
- [alembic/env.py](https://github.com/smaramwbc/statewave/blob/main/alembic/env.py)
- [alembic/versions](https://github.com/smaramwbc/statewave/blob/main/alembic/versions)
- [server/db/engine.py](https://github.com/smaramwbc/statewave/blob/main/server/db/engine.py)
- [server/services/backup.py](https://github.com/smaramwbc/statewave/blob/main/server/services/backup.py)
- [alembic/versions/0016_memory_status_tombstoned.py](https://github.com/smaramwbc/statewave/blob/main/alembic/versions/0016_memory_status_tombstoned.py)
</details>

# Database Schema & Migrations

## Overview

The Statewave application uses PostgreSQL as its primary data store, managed through SQLAlchemy ORM with Alembic for schema migrations. The database layer provides persistent storage for the core domain entities: **Subjects**, **Episodes**, **Memories**, **Resolutions**, and **Snapshots**.

All tables use UUID v4 as primary keys and include automatic timestamp tracking (`created_at`, `updated_at`) with timezone-aware `DateTime(timezone=True)` columns. The schema is designed to support multi-tenant isolation through a `tenant_id` column on relevant entities.

资料来源：[server/db/tables.py]()

## Architecture

```mermaid
graph TD
    subgraph "Database Layer"
        T[PostgreSQL]
        ORM[SQLAlchemy ORM]
        MIG[Alembic Migrations]
    end
    
    subgraph "Core Tables"
        SUBJ[subjects]
        EP[episodes]
        MEM[memories]
        RES[resolutions]
        SNAP[snapshots]
        POL[policy_bundles]
        SES[sessions]
    end
    
    T --> ORM
    ORM --> SUBJ
    ORM --> EP
    ORM --> MEM
    ORM --> RES
    ORM --> SNAP
    ORM --> POL
    ORM --> SES
    
    MIG -.->|manage schema| T
```

## Core Table Definitions

### subjects

The central entity representing a user, entity, or concept that episodes and memories are associated with.

| Column | Type | Nullable | Default | Description |
|--------|------|----------|---------|-------------|
| `id` | UUID | No | `uuid.uuid4()` | Primary key |
| `tenant_id` | String(256) | No | — | Tenant isolation |
| `name` | String(512) | Yes | — | Display name |
| `metadata_` | JSONB | No | `{}` | Flexible metadata |
| `version` | Integer | No | `1` | Optimistic locking |
| `created_at` | DateTime | No | `server_default=func.now()` | Creation timestamp |
| `updated_at` | DateTime | No | `server_default + onupdate` | Last modification |

资料来源：[server/db/tables.py]()

### episodes

Immutable event records representing interactions, activities, or data points fed into the system via connectors.

| Column | Type | Nullable | Default | Description |
|--------|------|----------|---------|-------------|
| `id` | UUID | No | Auto | Primary key |
| `subject_id` | UUID | No | — | FK → subjects |
| `tenant_id` | String(256) | No | — | Tenant isolation |
| `source` | String(64) | No | — | Origin (e.g., `slack`, `github`, `mcp`) |
| `type` | String(64) | No | — | Event type (e.g., `doc_section`, `chat_message`) |
| `payload` | JSONB | No | — | Raw event data |
| `metadata_` | JSONB | No | `{}` | Extracted metadata |
| `provenance` | JSONB | No | `{}` | Source attribution (hashes, URLs) |
| `occurred_at` | DateTime | Yes | — | When the event actually occurred |
| `created_at` | DateTime | No | Auto | Ingestion timestamp |
| `session_id` | UUID | Yes | — | Optional session grouping |

资料来源：[server/db/tables.py]()
资料来源：[server/services/context.py]()

### memories

Compiled, semantically indexed summaries derived from episodes. Memories have temporal validity windows and confidence scores.

| Column | Type | Nullable | Default | Description |
|--------|------|----------|---------|-------------|
| `id` | UUID | No | Auto | Primary key |
| `subject_id` | UUID | No | — | FK → subjects |
| `tenant_id` | String(256) | No | — | Tenant isolation |
| `kind` | String(32) | No | — | Memory category (e.g., `episode_summary`, `profile_fact`) |
| `content` | Text | No | — | Full memory content |
| `summary` | String(512) | Yes | — | Abbreviated summary |
| `confidence` | Float | No | — | LLM-assigned confidence (0.0-1.0) |
| `valid_from` | DateTime | No | — | When the memory becomes relevant |
| `valid_to` | DateTime | Yes | — | When the memory expires (TTL) |
| `source_episode_ids` | Array[UUID] | Yes | — | Episodes this memory was compiled from |
| `embedding` | Array[Float] | Yes | — | Vector embedding for semantic search |
| `metadata_` | JSONB | No | `{}` | Additional metadata |
| `status` | String(32) | No | — | `active`, `superseded`, or `tombstoned` |
| `sensitivity_labels` | Array[String] | Yes | — | Privacy/classification labels |
| `created_at` | DateTime | No | Auto | Creation timestamp |
| `updated_at` | DateTime | No | Auto | Last modification |

资料来源：[server/db/tables.py]()

**Status Values:**

| Status | Description |
|--------|-------------|
| `active` | Currently valid and usable |
| `superseded` | Replaced by a newer memory covering the same scope |
| `tombstoned` | Soft-deleted, retained for audit trail |

资料来源：[alembic/versions/0016_memory_status_tombstoned.py]()

### resolutions

Tracks issue or conversation resolution state per session.

| Column | Type | Nullable | Default | Description |
|--------|------|----------|---------|-------------|
| `id` | UUID | No | Auto | Primary key |
| `subject_id` | UUID | No | — | FK → subjects |
| `session_id` | UUID | No | — | Session identifier |
| `tenant_id` | String(256) | No | — | Tenant isolation |
| `status` | String(32) | No | — | Resolution status |
| `resolution_summary` | Text | Yes | — | Summary of resolution |
| `resolved_at` | DateTime | Yes | — | When resolution occurred |
| `metadata_` | JSONB | No | `{}` | Additional metadata |
| `created_at` | DateTime | No | Auto | Creation timestamp |
| `updated_at` | DateTime | No | Auto | Last modification |

资料来源：[server/db/tables.py]()
资料来源：[server/services/snapshots.py]()

### subject_snapshots

Immutable snapshots capturing a point-in-time state of a subject for backup/restore purposes.

| Column | Type | Nullable | Default | Description |
|--------|------|----------|---------|-------------|
| `id` | UUID | No | Auto | Primary key |
| `name` | String(512) | No | — | Snapshot name |
| `version` | Integer | No | — | Snapshot version |
| `source_subject_id` | UUID | No | — | Original subject being snapshotted |
| `episode_count` | Integer | No | — | Number of episodes captured |
| `memory_count` | Integer | No | — | Number of memories captured |
| `metadata_` | JSONB | No | `{}` | Snapshot metadata |
| `created_at` | DateTime | No | Auto | Creation timestamp |

资料来源：[server/db/tables.py]()

### policy_bundles

Immutable policy YAML bundles content-addressed by `bundle_hash`.

| Column | Type | Nullable | Default | Description |
|--------|------|----------|---------|-------------|
| `id` | UUID | No | Auto | Primary key |
| `bundle_hash` | String(64) | No | — | SHA-256 content hash (indexed) |
| `yaml_content` | Text | No | — | Full YAML policy content |
| `active` | Boolean | No | `False` | Whether this bundle is active |
| `tenant_id` | String(256) | Yes | — | Tenant isolation |

资料来源：[server/db/tables.py]()

**Design Note:** Composite unique index on `(tenant_id, bundle_hash) NULLS NOT DISTINCT` ensures one bundle per (scope, content) combination while allowing multiple tenants to install the same policy independently.

## Migration System

Statewave uses **Alembic** for database schema versioning. Migrations are located in the `alembic/versions/` directory and follow a sequential naming convention (e.g., `0016_memory_status_tombstoned.py`).

### Migration Workflow

```mermaid
graph LR
    A[Development] --> B[Create Migration]
    B --> C[Review SQL]
    C --> D[Apply: alembic upgrade head]
    D --> E[Production Deploy]
    E --> F[Rollback if needed: alembic downgrade]
```

### Migration Naming Convention

Migrations use the format `XXXX_<short_description>.py` where `XXXX` is a zero-padded sequence number. This ensures deterministic ordering across distributed development.

### Migration Example: Status Rename

The migration `0016_memory_status_tombstoned.py` demonstrates the migration pattern:

```python
"""memories.status — rename `deleted` to `tombstoned` (defensive)

Revision ID: 0016_memory_status_tombstoned
Revises: 0015_episode_occurred_at
Create Date: 2026-05-10
```

**Purpose:**
1. Normalize any existing `deleted` values to `tombstoned`
2. Align vocabulary with issue #49 (State-assembly receipts specify `supersession_status: active | superseded | tombstoned`)
3. Provide a visible breadcrumb for future developers tracing the change

资料来源：[alembic/versions/0016_memory_status_tombstoned.py]()

### Key Migration Characteristics

| Characteristic | Description |
|----------------|-------------|
| **Reversible** | All migrations include `downgrade` paths |
| **Idempotent checks** | Migrations verify existing state before modifying |
| **Server defaults** | Timestamps use `server_default=func.now()` for consistency |
| **Zero-downtime aware** | Helm chart runs pre-upgrade Jobs for migrations |

## Data Import/Export

The backup service (`server/services/backup.py`) provides import/export functionality that serializes and deserializes subjects, episodes, and memories.

### Import Process

```mermaid
graph TD
    A[JSON Archive] --> B[Parse episodes_data]
    B --> C[Parse memories_data]
    C --> D[Resolve subject_id]
    D --> E[Insert Episodes]
    E --> F[Insert Memories]
    F --> G[Return Import Result]
    
    E -->|preserve_ids| E1[Use original UUIDs]
    E -->|new_ids| E2[Generate new UUIDs]
```

**Import Flow:**
1. Validate archive structure
2. Resolve target `subject_id` based on `conflict_strategy`
3. Insert episodes with optional ID preservation
4. Insert memories with source episode ID remapping
5. Commit transaction

资料来源：[server/services/backup.py]()

### Snapshot Restore Process

Snapshots preserve the complete state of a subject including temporal offsets:

```python
# Timestamp shifting during restore
time_shift = restore_time - snapshot.created_at
valid_from = mem_valid_from + time_shift
valid_to = (mem_valid_to + time_shift) if mem_valid_to else None
```

The `metadata_` field receives `"restored_from_snapshot": str(snapshot_id)` for audit tracking.

资料来源：[server/services/snapshots.py]()

## Schema Design Patterns

### Temporal Modeling

Memories use `valid_from` and `valid_to` columns for temporal validity:

```python
def compute_valid_to(kind: str, valid_from: datetime, ttl: dict) -> datetime | None:
    """Compute expiration based on memory kind TTL."""
    ttl_hours = ttl.get(kind, DEFAULT_TTL_HOURS)
    return valid_from + timedelta(hours=ttl_hours)
```

资料来源：[server/services/compilers/heuristic.py]()

### Temporal Anchoring

The system extracts temporal anchors from episodes with priority:

1. `payload.event_time` — explicit client override
2. `payload.messages[0].timestamp` — first message timestamp in chat payloads
3. `created_at` — fallback to ingestion time

资料来源：[server/services/compilers/heuristic.py]()

### JSONB Flexibility

Columns like `payload`, `metadata_`, and `provenance` use PostgreSQL's `JSONB` type for schema flexibility:

- **payload**: Raw event data varying by source connector
- **metadata_**: Extracted structured data
- **provenance**: Source attribution with content hashes

This allows the system to evolve connectors without schema migrations.

### Composite Indexes

Key indexes for query performance:

| Index | Columns | Purpose |
|-------|---------|---------|
| `policy_bundles.bundle_hash` | `bundle_hash` | Content-addressed lookups |
| `memories.subject_id + kind` | (implied) | Subject memory filtering |
| `episodes.subject_id + occurred_at` | (implied) | Timeline queries |

## See Also

- [API v1 Contract](https://github.com/smaramwbc/statewave-docs/blob/main/api/v1-contract.md) — Database operations via REST API
- [Deployment Guide](https://github.com/smaramwbc/statewave-docs/blob/main/deployment/guide.md) — Database configuration
- [Helm Chart Configuration](../helm/statewave/README.md) — Migration job settings

---

<a id='page-api-endpoints'></a>

## API Endpoints Reference

### 相关页面

相关主题：[Context Assembly & Ranking](#page-context-assembly)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [server/api/episodes.py](https://github.com/smaramwbc/statewave/blob/main/server/api/episodes.py)
- [server/api/memories.py](https://github.com/smaramwbc/statewave/blob/main/server/api/memories.py)
- [server/api/context.py](https://github.com/smaramwbc/statewave/blob/main/server/api/context.py)
- [server/api/subjects.py](https://github.com/smaramwbc/statewave/blob/main/server/api/subjects.py)
- [server/api/resolutions.py](https://github.com/smaramwbc/statewave/blob/main/server/api/resolutions.py)
- [server/api/handoff.py](https://github.com/smaramwbc/statewave/blob/main/server/api/handoff.py)
- [server/api/sla.py](https://github.com/smaramwbc/statewave/blob/main/server/api/sla.py)
- [server/api/admin.py](https://github.com/smaramwbc/statewave/blob/main/server/api/admin.py)
- [server/services/context.py](https://github.com/smaramwbc/statewave/blob/main/server/services/context.py)
- [server/schemas/responses.py](https://github.com/smaramwbc/statewave/blob/main/server/schemas/responses.py)
</details>

# API Endpoints Reference

## Overview

The Statewave API provides a comprehensive set of RESTful endpoints for managing agent memory, tracking session context, and enabling multi-tenant workspace isolation. The API is built on FastAPI and follows OpenAPI standards with automatic documentation at `/docs` (Swagger UI) and `/redoc` (ReDoc).

**Base URL:** `http://localhost:8100`

**API Version:** v1

All endpoints support multi-tenant isolation through the `X-Tenant-ID` header, configurable via the `STATEWAVE_TENANT_HEADER` environment variable. The API enforces query-scoped data isolation for multi-tenant deployments. 资料来源：[README.md](https://github.com/smaramwbc/statewave/blob/main/README.md)

---

## Health & Readiness

### Liveness Check

```
GET /healthz
GET /health
```

Returns `200 OK` when the service is running. Use this for basic liveness probes in container orchestration.

### Readiness Check

```
GET /readyz
GET /ready
```

Returns `200 OK` when the service is ready to accept traffic, including database connectivity validation.

资料来源：[README.md](https://github.com/smaramwbc/statewave/blob/main/README.md)

---

## Episodes API

Episodes are the atomic, append-only units of memory ingestion. Each episode represents a discrete event, message, or interaction associated with a subject.

### Ingest Single Episode

```
POST /v1/episodes
```

Ingests a single episode under a subject. The episode is stored with provenance metadata and becomes available for memory compilation.

**Request Body:**
```json
{
  "subject_id": "string (UUID)",
  "session_id": "string (optional)",
  "source": "string (e.g., 'live-chat', 'mcp-server', 'github')",
  "type": "string (e.g., 'message', 'doc_section', 'issue')",
  "payload": {
    "messages": [...],
    "event_time": "ISO8601 datetime (optional)"
  },
  "metadata": {},
  "provenance": {
    "content_hash": "string (optional)"
  }
}
```

### Batch Ingest Episodes

```
POST /v1/episodes/batch
```

Ingests up to 100 episodes in a single request. This is the recommended endpoint for bulk data loading or connector imports.

**Request Body:**
```json
{
  "episodes": [
    { /* Episode object */ }
  ]
}
```

**Constraints:**
- Maximum 100 episodes per batch
- Each episode requires a valid `subject_id`
- All episodes are processed atomically

资料来源：[server/api/episodes.py](https://github.com/smaramwbc/statewave/blob/main/server/api/episodes.py)

---

## Memories API

Memories are compiled, queryable representations derived from episodes through the heuristic or LLM compiler.

### Compile Memories

```
POST /v1/memories/compile
```

Triggers memory compilation from episodes. This operation is idempotent—re-running compiles only processes new episodes.

**Request Body:**
```json
{
  "subject_id": "string (UUID)",
  "force": false
}
```

### Search Memories

```
GET /v1/memories/search
```

Searches memories by kind, text content, or semantic similarity.

**Query Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `subject_id` | string | required | Target subject UUID |
| `kind` | string | - | Filter by memory kind (e.g., `episode_summary`, `profile_fact`) |
| `q` | string | - | Full-text search query |
| `limit` | int | 50 | Maximum results (1-200) |
| `offset` | int | 0 | Pagination offset |

### Memory Evolution Tracking

The system maintains memory lineage through `source_episode_ids` relationships, enabling:
- **Supersession**: When new memories deprecate older ones
- **Sibling relationships**: Memories derived from the same source episodes
- **Provenance tracing**: Track which episodes contributed to each memory

资料来源：[server/api/memories.py](https://github.com/smaramwbc/statewave/blob/main/server/api/memories.py)

---

## Context API

The context API assembles ranked, token-bounded context bundles optimized for LLM consumption.

### Assemble Context

```
POST /v1/context
```

Generates a ranked context bundle suitable for passing to an LLM. The system considers:
- Memory relevance and confidence scores
- Token budget constraints (configurable via `STATEWAVE_DEFAULT_MAX_CONTEXT_TOKENS`)
- Temporal validity of memories

**Request Body:**
```json
{
  "subject_id": "string (UUID)",
  "query": "string (optional)",
  "max_tokens": 4000,
  "include_episodes": false,
  "kinds": ["episode_summary", "profile_fact"]
}
```

**Response:**
```json
{
  "subject_id": "string",
  "query": "string",
  "context": "string (assembled text)",
  "token_count": 1234,
  "memories_included": [
    {
      "id": "string",
      "kind": "string",
      "content": "string",
      "summary": "string",
      "confidence": 0.8
    }
  ]
}
```

The context assembly uses a scoring algorithm that weighs relevance, recency, and confidence to produce optimal results within the token budget. 资料来源：[server/services/context.py](https://github.com/smaramwbc/statewave/blob/main/server/services/context.py)

---

## Subjects API

Subjects are the primary entity representing a user, session, or workspace that maintains memory.

### List Subjects

```
GET /v1/subjects
```

Returns all subjects with episode and memory counts.

**Query Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `tenant_id` | string | - | Filter by tenant (for multi-tenant deployments) |
| `limit` | int | 50 | Results per page (1-200) |
| `offset` | int | 0 | Pagination offset |

### Delete Subject

```
DELETE /v1/subjects/{id}
```

Permanently deletes all episodes, memories, and resolutions for a subject. This operation is irreversible.

**Path Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | UUID | Subject identifier |

### Get Subject Health

```
GET /v1/subjects/{id}/health
```

Returns a customer health score with explainable factors based on recent activity, resolution rates, and memory density.

### Get Subject SLA

```
GET /v1/subjects/{id}/sla
```

Returns SLA metrics including:
- Response time
- Resolution time
- SLA breaches

资料来源：[server/api/subjects.py](https://github.com/smaramwbc/statewave/blob/main/server/api/subjects.py)

---

## Timeline API

### Get Subject Timeline

```
GET /v1/timeline
```

Returns a chronological timeline of events for a subject.

**Query Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `subject_id` | UUID | required | Target subject |
| `session_id` | string | - | Filter by session |
| `limit` | int | 50 | Maximum events |
| `offset` | int | 0 | Pagination offset |

**Response includes:**
- Episode events with full payload and metadata
- Resolution events with status transitions
- Temporal metadata (first/last message timestamps)
- SLA breach indicators

资料来源：[server/api/timeline.py](https://github.com/smaramwbc/statewave/blob/main/server/api/timeline.py)

---

## Resolutions API

Resolutions track issue resolution state per session, enabling workflow tracking and SLA measurement.

### Create Resolution

```
POST /v1/resolutions
```

Records a resolution event for a session.

**Request Body:**
```json
{
  "subject_id": "string (UUID)",
  "session_id": "string",
  "status": "resolved | escalated | pending",
  "resolution_summary": "string (optional)",
  "metadata": {}
}
```

### List Resolutions

```
GET /v1/resolutions
```

Lists resolutions for a subject.

**Query Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `subject_id` | UUID | required | Target subject |
| `session_id` | string | - | Filter by session |
| `status` | string | - | Filter by status |

资料来源：[server/api/resolutions.py](https://github.com/smaramwbc/statewave/blob/main/server/api/resolutions.py)

---

## Handoff API

### Generate Handoff Context

```
POST /v1/handoff
```

Generates a compact, self-contained context pack for transferring conversation context between agents or sessions.

**Request Body:**
```json
{
  "subject_id": "string (UUID)",
  "session_id": "string (optional)",
  "include_full_history": false,
  "max_tokens": 2000
}
```

This endpoint produces a lightweight JSON payload containing:
- Recent memory summaries
- Pending context items
- Session state indicators
- Structured for immediate agent consumption

资料来源：[server/api/handoff.py](https://github.com/smaramwbc/statewave/blob/main/server/api/handoff.py)

---

## SLA API

### Get SLA Metrics

```
GET /v1/subjects/{id}/sla
```

Returns structured SLA metrics for monitoring and reporting.

**Response:**
```json
{
  "subject_id": "string",
  "period": "string (e.g., '30d')",
  "response_time_p50": 120.5,
  "response_time_p95": 450.0,
  "resolution_time_avg": 1800.0,
  "total_sessions": 150,
  "resolved_sessions": 142,
  "breach_count": 8,
  "breach_rate": 0.053
}
```

资料来源：[server/api/sla.py](https://github.com/smaramwbc/statewave/blob/main/server/api/sla.py)

---

## Admin API

The admin endpoints provide extended visibility and management capabilities for system operators.

### List Episodes (Admin)

```
GET /admin/subjects/{subject_id}/episodes
```

Administrative endpoint for listing episodes with pagination.

### List Citing Memories

```
GET /admin/subjects/{subject_id}/episodes/{episode_id}/citing-memories
```

Returns all memories that cite a specific episode as a source. This enables reverse provenance lookup.

### List All Subjects (Admin)

```
GET /admin/subjects
```

Administrative listing of all subjects with optional pagination.

### Import Subject Data

```
POST /admin/import
```

Imports episodes and memories for a subject from JSONL format.

### Export Subject Data

```
GET /admin/subjects/{id}/export
```

Exports all subject data (episodes, memories, resolutions) to JSONL format.

### Create Snapshot

```
POST /admin/snapshots
```

Creates a point-in-time snapshot of a subject's state.

### Restore Snapshot

```
POST /admin/snapshots/{id}/restore
```

Restores a subject from a previously created snapshot.

### List Snapshots

```
GET /admin/snapshots
```

Lists available snapshots with metadata.

资料来源：[server/api/admin.py](https://github.com/smaramwbc/statewave/blob/main/server/api/admin.py)

---

## Request/Response Schemas

### Core Request Schema Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `subject_id` | UUID | Yes | Target subject identifier |
| `session_id` | string | No | Associated session |
| `source` | string | No | Origin of data (e.g., 'mcp-server', 'github') |
| `type` | string | No | Data type discriminator |
| `payload` | object | Yes | Type-specific payload data |
| `metadata` | object | No | Arbitrary metadata |
| `provenance` | object | No | Source tracking information |

### Core Response Schema Fields

| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | Unique resource identifier |
| `created_at` | ISO8601 | Creation timestamp |
| `updated_at` | ISO8601 | Last modification timestamp |
| `status` | string | Resource status (active, archived, etc.) |

资料来源：[server/schemas/responses.py](https://github.com/smaramwbc/statewave/blob/main/server/schemas/responses.py)

---

## API Flow Diagram

```mermaid
graph TD
    A[Client] -->|POST /v1/episodes| B[Episodes API]
    A -->|POST /v1/memories/compile| C[Memory Compiler]
    A -->|GET /v1/memories/search| D[Memory Search]
    A -->|POST /v1/context| E[Context Assembler]
    
    B --> F[(PostgreSQL<br/>episodes table)]
    C --> G[Heuristic or LLM Compiler]
    G --> H[(PostgreSQL<br/>memories table)]
    D --> H
    E --> H
    E --> I[Ranked Context Bundle]
    
    A -->|GET /v1/timeline| J[Timeline Service]
    J --> F
    J --> H
    
    A -->|POST /v1/resolutions| K[(PostgreSQL<br/>resolutions table)]
    
    A -->|POST /v1/handoff| L[Handoff Generator]
    L --> H
    L --> M[Compact Handoff Pack]
```

---

## Environment Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `STATEWAVE_DATABASE_URL` | required | PostgreSQL connection string |
| `STATEWAVE_API_KEY` | - | API authentication key |
| `STATEWAVE_LITELLM_API_KEY` | - | LLM provider API key |
| `STATEWAVE_TENANT_HEADER` | `X-Tenant-ID` | Multi-tenant isolation header |
| `STATEWAVE_REQUIRE_TENANT` | `false` | Reject requests without tenant header |
| `STATEWAVE_DEFAULT_MAX_CONTEXT_TOKENS` | `4000` | Token budget for context assembly |
| `STATEWAVE_RATE_LIMIT` | `0` | Requests per minute per IP |
| `STATEWAVE_RATE_LIMIT_STRATEGY` | `distributed` | Rate limiting strategy |
| `STATEWAVE_WEBHOOK_URL` | - | Webhook callback URL |
| `STATEWAVE_CORS_ORIGINS` | `["*"]` | Allowed CORS origins |

资料来源：[README.md](https://github.com/smaramwbc/statewave/blob/main/README.md)

---

## OpenAPI Documentation

Interactive API documentation is available at:

| URL | Format |
|-----|--------|
| `http://localhost:8100/docs` | Swagger UI |
| `http://localhost:8100/redoc` | ReDoc |

Full API contract documentation: [API v1 contract](https://github.com/smaramwbc/statewave-docs/blob/main/api/v1-contract.md)

---

<a id='page-context-assembly'></a>

## Context Assembly & Ranking

### 相关页面

相关主题：[API Endpoints Reference](#page-api-endpoints), [Embedding Services](#page-embeddings), [Governance - Receipts & Policy Engine](#page-receipts-policy)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [server/services/context.py](https://github.com/smaramwbc/statewave/blob/main/server/services/context.py)
- [server/api/context.py](https://github.com/smaramwbc/statewave/blob/main/server/api/context.py)
- [server/services/compilers/heuristic.py](https://github.com/smaramwbc/statewave/blob/main/server/services/compilers/heuristic.py)
- [server/api/admin.py](https://github.com/smaramwbc/statewave/blob/main/server/api/admin.py)
- [server/services/snapshots.py](https://github.com/smaramwbc/statewave/blob/main/server/services/snapshots.py)
- [README.md](https://github.com/smaramwbc/statewave/blob/main/README.md)
</details>

# Context Assembly & Ranking

Context Assembly & Ranking is the core mechanism that transforms raw episodic memory into a curated, token-bounded context bundle for LLM consumption. When an agent queries Statewave for context, the system retrieves relevant memories and episodes, scores them by relevance, and assembles them within a configurable token budget.

## Architecture Overview

The context assembly system operates as a multi-stage pipeline:

1. **Retrieval** — Query memories and episodes by subject, kind, text, or semantic similarity
2. **Scoring** — Assign relevance scores to each item using configurable strategies
3. **Ranking** — Sort items by score and enforce token budget constraints
4. **Assembly** — Build the final context bundle with proper formatting and ordering

```mermaid
graph TD
    A[Client Request: POST /v1/context] --> B[ContextService.assemble]
    B --> C[Retrieve Memories by Query]
    B --> D[Retrieve Episodes by Query]
    C --> E[_ScoredItem Scoring Loop]
    D --> E
    E --> F[Apply Ranking Strategy]
    F --> G[Token Budget Enforcement]
    G --> H[Context Bundle Response]
    
    E -->|score, kind, text, section| E
```

## Core Data Model

### `_ScoredItem` Class

The fundamental unit used throughout the ranking pipeline is the `_ScoredItem` class, defined in `server/services/context.py`:

```python
class _ScoredItem:
    """An item (memory or episode) with its computed relevance score."""

    __slots__ = ("score", "kind", "memory_row", "episode_row", "text", "section")

    def __init__(
        self,
        score: float,
        kind: str,
        text: str,
        section: str,
        memory_row: Any = None,
        episode_row: Any = None,
    ) -> None:
        self.score = score
        self.kind = kind
        self.memory_row = memory_row
        self.episode_row = episode_row
        self.text = text
        self.section = section
```

| Attribute | Type | Description |
|-----------|------|-------------|
| `score` | `float` | Computed relevance score (higher = more relevant) |
| `kind` | `str` | Item type: `"memory"` or `"episode"` |
| `memory_row` | `Any` | `MemoryRow` instance when `kind == "memory"` |
| `episode_row` | `Any` | `EpisodeRow` instance when `kind == "episode"` |
| `text` | `str` | Extracted text content for scoring |
| `section` | `str` | Section identifier for display/breadcrumb |

资料来源：[server/services/context.py:49-67]()

### Memory Response Model

When context is assembled, memories are returned with full provenance:

```python
MemoryResponse(
    id=m.id,
    subject_id=m.subject_id,
    kind=m.kind,
    content=m.content,
    summary=m.summary,
    confidence=m.confidence,
    valid_from=m.valid_from,
    valid_to=m.valid_to,
    source_episode_ids=m.source_episode_ids or [],
    metadata=m.metadata_,
    status=m.status,
    sensitivity_labels=list(m.sensitivity_labels or []),
    created_at=m.created_at,
    updated_at=m.updated_at,
)
```

资料来源：[server/api/timeline.py:1-20]()

## Context Assembly Flow

### Endpoint: `POST /v1/context`

Assembles a ranked, token-bounded context bundle from a subject's episodic memory.

```mermaid
sequenceDiagram
    participant Client
    participant API as /v1/context
    participant ContextService
    participant MemoryStore
    participant EpisodeStore

    Client->>API: POST /v1/context {subject_id, query, max_tokens}
    API->>ContextService: assemble(request)
    ContextService->>MemoryStore: search_memories(query)
    MemoryStore-->>ContextService: [MemoryRow...]
    ContextService->>EpisodeStore: search_episodes(query)
    EpisodeStore-->>ContextService: [EpisodeRow...]
    ContextService->>ContextService: score_and_rank(items)
    ContextService->>ContextService: enforce_token_budget(items, max_tokens)
    ContextService-->>API: ContextResponse
    API-->>Client: 200 OK
```

### Token Budget Enforcement

The system enforces a configurable token budget via the `STATEWAVE_DEFAULT_MAX_CONTEXT_TOKENS` environment variable (default: 4000 tokens). This ensures that:

- Large subjects don't overwhelm LLM context windows
- The most relevant items are prioritized
- Lower-scored items are truncated when budget is exhausted

| Parameter | Default | Description |
|-----------|---------|-------------|
| `STATEWAVE_DEFAULT_MAX_CONTEXT_TOKENS` | `4000` | Default token budget for context assembly |

资料来源：[README.md:1-100]()

## Ranking Strategies

The ranking system supports multiple strategies that can be applied in combination:

### 1. Semantic Similarity Ranking

For queries with semantic intent, memories are ranked by vector similarity against the query embedding. This is the default strategy when using `kind="semantic"` in search requests.

### 2. Confidence-Based Ranking

Memories include a `confidence` field (0.0–1.0) that reflects the compiler's certainty. Higher-confidence memories are ranked higher by default.

| Memory Kind | Typical Confidence Range |
|-------------|-------------------------|
| `episode_summary` | 0.8 |
| `profile_fact` | 0.6 |
| `key_event` | 0.7–0.9 |
| Custom | Variable |

资料来源：[server/services/compilers/heuristic.py:1-100]()

### 3. Temporal Recency Ranking

For timeline-sensitive queries, items can be weighted by recency (`valid_from`, `created_at`). Recent memories receive higher ranking weights.

### 4. Composite Scoring

The `_ScoredItem` system enables composite scoring:

```python
final_score = (
    semantic_weight * semantic_score +
    confidence_weight * confidence +
    recency_weight * recency_score
)
```

## Memory Kinds and Their Role in Context

Statewave supports structured memory kinds that inform ranking behavior:

| Kind | Description | Ranking Priority |
|------|-------------|-------------------|
| `episode_summary` | Auto-generated summary of an episode | High |
| `profile_fact` | Extracted subject facts | Medium-High |
| `key_event` | Significant events in subject history | High |
| `resolution` | Issue resolution tracking | Context-dependent |
| Custom kinds | User-defined memory types | User-defined |

资料来源：[server/services/compilers/heuristic.py:50-100]()

## Configuration Options

### Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `STATEWAVE_DEFAULT_MAX_CONTEXT_TOKENS` | `4000` | Token budget for context assembly |
| `STATEWAVE_LITELLM_API_KEY` | — | LLM API key for embedding-based ranking |
| `STATEWAVE_EMBEDDING_PROVIDER` | `litellm` | Provider for semantic embeddings |

资料来源：[README.md:100-150]()

### Helm Chart Configuration

For Kubernetes deployments:

```yaml
compiler:
  type: llm  # or 'heuristic' for no-LLM mode
embedding:
  provider: litellm  # or 'stub' for demo mode
```

资料来源：[helm/statewave/README.md:1-50]()

## API Response Structure

### Success Response: `200 OK`

```json
{
  "subject_id": "uuid",
  "query": "user query string",
  "memories": [
    {
      "id": "uuid",
      "subject_id": "uuid",
      "kind": "episode_summary",
      "content": "...",
      "summary": "...",
      "confidence": 0.8,
      "valid_from": "2024-01-15T10:00:00Z",
      "valid_to": "2024-01-20T10:00:00Z",
      "source_episode_ids": ["uuid1", "uuid2"],
      "metadata": {},
      "status": "active",
      "sensitivity_labels": [],
      "created_at": "2024-01-15T10:30:00Z",
      "updated_at": "2024-01-15T10:30:00Z"
    }
  ],
  "episodes": [],
  "total_tokens": 3500,
  "truncated": false
}
```

## Snapshot Integration

Context assembly respects snapshot-restored memories. When memories are restored from snapshots, they carry provenance:

```python
metadata_={
    **mem.metadata_,
    "restored_from_snapshot": str(snapshot_id),
}
```

资料来源：[server/services/snapshots.py:1-50]()

## Memory Evolution and Context

The memory evolution system tracks superseding relationships:

```python
class MemoryEvolutionResponse(BaseModel):
    memory_id: str
    status: str
    created_at: str
    superseding_memory: Optional[MemoryRelation]
    superseded_memories: list[MemoryRelation]
    sibling_memories: list[MemoryRelation]
    source_episode_count: int
```

When assembling context, superseded memories may be excluded or marked, ensuring agents see the most current information.

资料来源：[server/api/admin.py:1-100]()

## Evaluation and Testing

The `scripts/eval/eval_docs_support.py` module provides evaluation patterns for context quality:

```python
Question(
    task="How does context ranking work?",
    expected_doc_paths=["architecture/ranking.md"],
    expected_terms=["ranking", "score"],
)
```

This validates that the ranking system correctly surfaces relevant memories for queries.

资料来源：[scripts/eval/eval_docs_support.py:1-50]()

## Best Practices

1. **Set Appropriate Token Budgets**: Balance context richness against LLM limits. Start with 4000 and adjust based on model context window.

2. **Use Semantic Search for Complex Queries**: When users ask complex questions, semantic similarity ranking outperforms keyword matching.

3. **Leverage Memory Kinds**: Structure memories with appropriate kinds to enable kind-filtered context retrieval.

4. **Monitor Confidence Scores**: Low-confidence memories may indicate compilation issues or ambiguous data.

5. **Snapshot Before Major Changes**: Use snapshots to preserve context state before bulk operations.

## Related Endpoints

| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/v1/context` | Assemble ranked, token-bounded context bundle |
| `GET` | `/v1/memories/search` | Search by kind, text, or semantic similarity |
| `GET` | `/v1/timeline` | Chronological subject timeline |
| `GET` | `/v1/subjects` | List known subjects with episode/memory counts |

资料来源：[README.md:50-80]()

---

<a id='page-compilation-services'></a>

## Compilation Services

### 相关页面

相关主题：[System Architecture](#page-architecture)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [server/services/compiler.py](https://github.com/smaramwbc/statewave/blob/main/server/services/compiler.py)
- [server/services/compilers/heuristic.py](https://github.com/smaramwbc/statewave/blob/main/server/services/compilers/heuristic.py)
- [server/services/compilers/llm.py](https://github.com/smaramwbc/statewave/blob/main/server/services/compilers/llm.py)
- [server/services/conflicts.py](https://github.com/smaramwbc/statewave/blob/main/server/services/conflicts.py)
- [server/services/compile_jobs.py](https://github.com/smaramwbc/statewave/blob/main/server/services/compile_jobs.py)
</details>

# Compilation Services

Compilation Services transform raw episodes into structured memories within Statewave. This process extracts meaningful information from conversation events, resolves conflicts between overlapping facts, and produces durable knowledge representations that agents can retrieve during context assembly.

## Overview

Statewave's compilation pipeline operates as a multi-stage process that converts incoming episode data into memory entities. The system supports two distinct compilation modes — heuristic and LLM — allowing deployments to choose between performance and intelligence based on infrastructure constraints.

```mermaid
graph TD
    A[Episode Ingest] --> B{Compilation Mode}
    B -->|Heuristic| C[Heuristic Compiler]
    B -->|LLM| D[LLM Compiler]
    C --> E[Memory Rows]
    D --> E
    E --> F[Conflict Resolution]
    F --> G[Finalized Memories]
    
    H[Context Assembly] --> I[Memory Retrieval]
    G --> I
```

## Compilation Modes

Statewave ships with two compiler implementations, each suited to different operational contexts.

### Heuristic Compiler

The heuristic compiler performs rule-based extraction without external AI dependencies. It analyzes episode text using pattern matching and natural language heuristics to identify structured facts.

**Supported Memory Kinds:**

| Kind | Description | Confidence | TTL |
|------|-------------|------------|-----|
| `episode_summary` | Concise summary of episode content | 0.8 | 30 days |
| `profile_fact` | Extracted user profile attributes | 0.6 | 90 days |
| `preference` | User-stated preferences | 0.7 | 60 days |
| `fact` | General factual statements | 0.65 | 60 days |

The heuristic compiler processes each episode independently, extracting profile facts through pattern recognition and generating episode summaries through text truncation and normalization.

### LLM Compiler

The LLM compiler leverages large language models to perform semantic understanding of episodes. This mode produces higher-quality memories with better contextual awareness but requires external LLM infrastructure.

**Key Capabilities:**
- Multi-episode summarization across conversation turns
- Inference of implicit facts not explicitly stated
- Better handling of ambiguous or complex dialogue
- Cross-episode fact consolidation

## Temporal Anchoring

Every memory requires a temporal anchor to support time-bounded queries and historical reasoning. The compilation system determines validity windows using a priority cascade.

```mermaid
graph LR
    A[Temporal Anchor Priority] --> B[payload.event_time]
    B --> C[payload.messages[0].timestamp]
    C --> D[Episode created_at]
    
    E[compute_valid_to] --> F[Memory.valid_from]
    F --> G[Memory.valid_to]
```

**Anchor Resolution Order:**
1. Explicit `event_time` in episode payload (connector replay scenarios)
2. First message timestamp in chat-shaped payloads
3. Episode creation timestamp (fallback)

The `valid_to` date derives from the memory kind's configured TTL (time-to-live), ensuring automatic expiration of transient information.

## Conflict Resolution

When multiple episodes produce overlapping memories, the conflict resolution service determines which information takes precedence.

```mermaid
graph TD
    A[New Memory] --> B{Same Subject?}
    B -->|No| C[Insert Directly]
    B -->|Yes| D{Overlapping Time Range?}
    D -->|No| E[Insert Directly]
    D -->|Yes| F{Conflicting Content?}
    F -->|No| G[Insert as Sibling]
    F -->|Yes| H[Apply Resolution Strategy]
    
    H --> I{Strategy: Supersede}
    H --> J{Strategy: Merge}
    H --> K{Strategy: Retain Both}
    
    I --> L[Mark Older as Superseded]
    J --> M[Combine Content]
    K --> N[Link as Alternatives]
```

**Resolution Strategies:**

| Strategy | Behavior | Use Case |
|----------|----------|----------|
| `supersede` | Replace older conflicting memory | Corrected information |
| `merge` | Combine facts from both memories | Complementary details |
| `retain_both` | Keep both as alternatives | Ambiguous or opinion-based |

## Compilation Job Management

Long-running compilation tasks are managed through an asynchronous job system that prevents API timeouts and supports retry logic.

```mermaid
graph TD
    A[POST /v1/memories/compile] --> B{Currently Compiling?}
    B -->|Yes| C[Return 409 Conflict]
    B -->|No| D[Queue Job]
    D --> E[Job Created]
    E --> F[Background Processing]
    F --> G{Success?}
    G -->|Yes| H[Memories Created]
    G -->|No| I{Retryable?}
    I -->|Yes| J[Retry with Backoff]
    I -->|No| K[Mark Failed]
    J --> F
```

**Job States:**
- `pending` — Queued for processing
- `running` — Actively compiling
- `completed` — Successfully finished
- `failed` — Unrecoverable error
- `cancelled` — Manually aborted

## Data Models

### Episode to Memory Flow

```
EpisodeRow
├── id: UUID
├── subject_id: UUID
├── source: str (e.g., "mcp-server", "github", "statewave-docs")
├── type: str (e.g., "conversation", "doc_section", "issue")
├── payload: dict
│   ├── title: str
│   ├── text: str
│   ├── messages: list[dict]
│   └── ...
├── metadata_: dict
├── provenance: dict
│   └── content_hash: str
└── created_at: datetime

         ↓ Compilation

MemoryRow
├── id: UUID
├── subject_id: UUID
├── kind: str (e.g., "episode_summary", "profile_fact")
├── content: str
├── summary: str
├── confidence: float (0.0 - 1.0)
├── valid_from: datetime
├── valid_to: datetime
├── source_episode_ids: list[UUID]
├── metadata_: dict
├── status: str ("active", "superseded", "redacted")
├── sensitivity_labels: list[str]
├── embedding: vector (optional)
└── created_at, updated_at: datetime
```

## Configuration

### Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `STATEWAVE_COMPILER_TYPE` | `llm` | Compiler mode: `llm` or `heuristic` |
| `STATEWAVE_LLM_API_KEY` | — | API key for LLM provider |
| `STATEWAVE_LLM_MODEL` | `gpt-4o` | Model identifier for LLM compiler |
| `STATEWAVE_COMPILE_TIMEOUT` | `300` | Compilation timeout in seconds |

### Helm Chart Configuration

| Value | Default | Notes |
|-------|---------|-------|
| `compiler.type` | `llm` | `heuristic` for no-LLM mode |
| `llm.apiKey` | — | Required when `compiler.type=llm` |
| `llm.model` | `gpt-4o` | Override default model |

## Idempotency

Compilation is designed to be idempotent. Re-running compilation on episodes that have already been compiled will not create duplicate memories. The system tracks `source_episode_ids` to prevent reprocessing and uses content hashing in provenance metadata to detect unchanged source material.

This design supports:
- Safe re-compilation after configuration changes
- Incremental updates from connector replay
- Bootstrap and refresh workflows

## API Integration

Compilation is triggered through the REST API:

```bash
POST /v1/memories/compile
```

The endpoint accepts a `subject_id` and initiates asynchronous processing. Polling the job status or receiving webhooks (if configured) provides completion feedback.

**Request Example:**
```json
{
  "subject_id": "550e8400-e29b-41d4-a716-446655440000",
  "force": false
}
```

**Response:**
```json
{
  "status": "queued",
  "job_id": "660e8400-e29b-41d4-a716-446655440001"
}

---

<a id='page-embeddings'></a>

## Embedding Services

### 相关页面

相关主题：[Context Assembly & Ranking](#page-context-assembly)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [server/services/embeddings/litellm.py](https://github.com/smaramwbc/statewave/blob/main/server/services/embeddings/litellm.py)
- [server/services/embeddings/stub.py](https://github.com/smaramwbc/statewave/blob/main/server/services/embeddings/stub.py)
- [server/services/embeddings/query_cache.py](https://github.com/smaramwbc/statewave/blob/main/server/services/embeddings/query_cache.py)
- [server/services/embeddings/backfill.py](https://github.com/smaramwbc/statewave/blob/main/server/services/embeddings/backfill.py)
</details>

# Embedding Services

Embedding Services provide semantic vector representations for memories and episodes in Statewave, enabling similarity-based search, context ranking, and retrieval-augmented generation workflows.

## Overview

The embedding layer is a pluggable component that converts textual content into high-dimensional vector embeddings. These vectors power the semantic search capabilities of the `/v1/memories/search` endpoint and the relevance scoring in context assembly (`POST /v1/context`).

Statewave supports two embedding providers:

| Provider | Purpose | Use Case |
|----------|---------|----------|
| `litellm` | Real embeddings via LiteLLM proxy | Production deployments with LLM API access |
| `stub` | Zero vectors (all dimensions = 0) | Development, demos, or when embeddings are disabled |

资料来源：[helm/statewave/README.md](https://github.com/smaramwbc/statewave/blob/main/helm/statewave/README.md)

## Architecture

```mermaid
graph TD
    subgraph "Embedding Services Layer"
        LLM[LiteLLM Embeddings<br/>litellm.py]
        STUB[Stub Embeddings<br/>stub.py]
        CACHE[Query Cache<br/>query_cache.py]
    end
    
    subgraph "Backfill Operations"
        BF[Backfill Service<br/>backfill.py]
    end
    
    subgraph "Consumers"
        SEARCH[Memory Search<br/>/v1/memories/search]
        CONTEXT[Context Assembly<br/>/v1/context]
        COMPILER[Memory Compiler]
    end
    
    LLM --> CACHE
    STUB --> CACHE
    CACHE --> SEARCH
    CACHE --> CONTEXT
    CACHE --> COMPILER
    BF --> LLM
    BF --> STUB
```

## Configuration

Embedding behavior is controlled via environment variables:

| Environment Variable | Default | Description |
|---------------------|---------|-------------|
| `STATEWAVE_EMBEDDING_PROVIDER` | `litellm` | Embedding provider: `litellm` or `stub` |
| `STATEWAVE_LITELLM_API_KEY` | — | API key for LiteLLM |
| `STATEWAVE_LITELLM_BASE_URL` | — | Custom LiteLLM base URL (for proxy deployments) |
| `STATEWAVE_EMBEDDING_MODEL` | provider default | Specific embedding model to use |

资料来源：[helm/statewave/README.md](https://github.com/smaramwbc/statewave/blob/main/helm/statewave/README.md)

## Providers

### LiteLLM Provider

The LiteLLM provider (`server/services/embeddings/litellm.py`) routes embedding requests through the LiteLLM proxy library, which standardizes access to multiple LLM backends including OpenAI, Azure, Cohere, and custom providers.

**Key responsibilities:**

- Accept text input and return normalized embedding vectors
- Handle API authentication via `STATEWAVE_LITELLM_API_KEY`
- Support custom base URLs via `STATEWAVE_LITELLM_BASE_URL`
- Model selection via `STATEWAVE_EMBEDDING_MODEL`
- Normalize embedding dimensions across providers

### Stub Provider

The stub provider (`server/services/embeddings/stub.py`) returns zero vectors for all inputs. This is useful for:

- Development environments without API access
- Demo deployments where semantic search is not required
- Testing the rest of the system without embedding dependencies

资料来源：[server/services/embeddings/stub.py](https://github.com/smaramwbc/statewave/blob/main/server/services/embeddings/stub.py)

## Query Cache

The query cache (`server/services/embeddings/query_cache.py`) reduces redundant embedding computations by caching the results of previously generated embeddings.

### Cache Strategy

```mermaid
graph LR
    A[Embedding Request] --> B{Hit in Cache?}
    B -->|Yes| C[Return Cached Vector]
    B -->|No| D[Compute Embedding]
    D --> E[Store in Cache]
    E --> F[Return Vector]
    C --> F
```

### Cache Behavior

- Cache key is derived from the text content being embedded
- Cached vectors are returned directly on cache hit
- Reduces API calls and latency for repeated queries
- Particularly effective for search queries that repeat across requests

资料来源：[server/services/embeddings/query_cache.py](https://github.com/smaramwbc/statewave/blob/main/server/services/embeddings/query_cache.py)

## Backfill Operations

The backfill service (`server/services/embeddings/backfill.py`) handles bulk embedding regeneration for existing data.

### Use Cases

- **Model migration**: When switching embedding models, regenerate all existing embeddings
- **Corruption recovery**: Rebuild embeddings for data with missing or corrupted vectors
- **Schema changes**: When embedding schema or normalization changes

### Backfill Process

```mermaid
graph TD
    A[Start Backfill] --> B[Query Rows Missing Embeddings]
    B --> C{Process Batch}
    C -->|Has More| D[Fetch Batch]
    D --> E[Generate Embeddings]
    E --> F[Update Database]
    F --> C
    C -->|Complete| G[Backfill Complete]
```

资料来源：[server/services/embeddings/backfill.py](https://github.com/smaramwbc/statewave/blob/main/server/services/embeddings/backfill.py)

## Integration with Memory System

Embeddings are stored alongside memory records and used for relevance scoring:

```mermaid
graph LR
    subgraph "Memory Row Schema"
        M1[id]
        M2[content]
        M3[embedding]
        M4[source_episode_ids]
    end
    
    subgraph "Usage Flow"
        S[Search Query] --> E[Generate Query Embedding]
        E --> SC[Compute Similarity Scores]
        M3 --> SC
        SC --> R[Ranked Results]
    end
```

The `embedding` field in `MemoryRow` stores the vector representation of memory content, enabling:

1. **Semantic similarity search** via cosine similarity
2. **Context relevance scoring** during context assembly
3. **Deduplication** by detecting high-similarity memory candidates

资料来源：[server/services/context.py](https://github.com/smaramwbc/statewave/blob/main/server/services/context.py)

## API Endpoints Using Embeddings

| Endpoint | Embedding Usage |
|----------|-----------------|
| `GET /v1/memories/search` | Query embedding vs stored memory embeddings |
| `POST /v1/context` | Rank episode and memory candidates by relevance |
| `POST /v1/memories/compile` | Generate embeddings for new memories |

## Deployment Considerations

### Production Deployment

For production with semantic search enabled:

1. Set `STATEWAVE_EMBEDDING_PROVIDER=litellm`
2. Configure `STATEWAVE_LITELLM_API_KEY` with appropriate permissions
3. Optionally specify `STATEWAVE_EMBEDDING_MODEL` for a specific model
4. For proxy deployments, set `STATEWAVE_LITELLM_BASE_URL`

### Demo Mode

For demos or development without API costs:

1. Set `STATEWAVE_EMBEDDING_PROVIDER=stub`
2. Full functionality except semantic search (returns zero similarity)

资料来源：[helm/statewave/README.md](https://github.com/smaramwbc/statewave/blob/main/helm/statewave/README.md)

### Helm Configuration

```yaml
embedding:
  provider: litellm  # or "stub" for demo mode
  model: ""          # optional: specify model
```

## Related Services

- **Compiler Services**: Use embeddings during memory compilation
- **Context Assembly**: Uses embeddings for relevance scoring
- **Memory Search**: Primary consumer of embedding similarity
- **Snapshots**: Preserves embeddings when snapshotting subject state

资料来源：[server/services/snapshots.py](https://github.com/smaramwbc/statewave/blob/main/server/services/snapshots.py)

---

<a id='page-receipts-policy'></a>

## Governance - Receipts & Policy Engine

### 相关页面

相关主题：[Multi-tenancy & Security](#page-multi-tenancy), [Context Assembly & Ranking](#page-context-assembly)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [server/services/context.py](https://github.com/smaramwbc/statewave/blob/main/server/services/context.py) - Context assembly with receipt emission
- [server/services/handoff.py](https://github.com/smaramwbc/statewave/blob/main/server/services/handoff.py) - Handoff service with receipt support
- [server/schemas/responses.py](https://github.com/smaramwbc/statewave/blob/main/server/schemas/responses.py) - Response schemas including receipt fields
- [server/schemas/requests.py](https://github.com/smaramwbc/statewave/blob/main/server/schemas/requests.py) - Request schemas with receipt and policy parameters
- [server/db/tables.py](https://github.com/smaramwbc/statewave/blob/main/server/db/tables.py) - Database schema including PolicyBundleRow
</details>

# Governance - Receipts & Policy Engine

## Overview

The Governance layer in Statewave provides two interrelated mechanisms for operational accountability and data access control:

1. **State Assembly Receipts** — Immutable audit records that capture exactly which memories and episodes were included in a `/v1/context` or `/v1/handoff` response, enabling full traceability of AI-generated context.
2. **Policy Engine** — A content-addressed bundle system that enforces sensitivity-label-based filtering on memories before they appear in responses, with configurable enforcement modes.

These systems work together to ensure that when a context bundle is assembled for downstream AI consumption, there is a verifiable record of what was included, why it was included, and what policy rules were applied.

资料来源：[server/schemas/responses.py](https://github.com/smaramwbc/statewave/blob/main/server/schemas/responses.py)

---

## State Assembly Receipts

### Purpose

Receipts address the "black box" problem in RAG-based AI systems. When an agent receives a context bundle, receipts provide cryptographic proof of:

- Which specific memories and episodes were selected
- The ranking scores used for selection
- What policy filters were applied
- The token budget consumed
- Which policy bundle version governed the decision

This enables auditing, debugging, compliance reporting, and reproducibility of AI decisions that depend on Statewave's memory layer.

### Receipt Data Model

Receipts are persisted to the database and linked to the request via a unique `receipt_id`. The core receipt structure captures the complete assembly decision:

| Field | Type | Description |
|-------|------|-------------|
| `receipt_id` | `uuid.UUID` | Unique identifier for this receipt |
| `subject_id` | `str` | The subject whose context was assembled |
| `task` | `str` | The task/retrieval purpose |
| `tenant_id` | `str \| None` | Tenant context for multi-tenant deployments |
| `as_of` | `datetime` | Timestamp of the assembly request |
| `context_hash` | `str` | Hash of the assembled context for integrity verification |
| `context_size_bytes` | `int` | Size of the assembled context in bytes |
| `token_estimate` | `int` | Estimated token count consumed |
| `selected_memories` | `list` | Memory IDs with scores that were included |
| `selected_episodes` | `list` | Episode IDs with scores that were included |
| `policy_bundle_hash` | `str \| None` | Hash of the policy bundle applied |
| `policy_mode` | `str` | Mode used (`log_only` or `enforce`) |
| `filters_applied` | `list` | Policy rules that resulted in exclusions |
| `filters_skipped` | `list` | Policy rules not triggered |
| `query_id` | `str \| None` | External correlation ID |
| `task_id` | `str \| None` | Task correlation ID |
| `parent_receipt_id` | `str \| None` | For nested/derived requests |
| `caller_id` | `str \| None` | Identity of the calling service |
| `caller_type` | `str \| None` | Type of caller (service, user, etc.) |

资料来源：[server/services/context.py](https://github.com/smaramwbc/statewave/blob/main/server/services/context.py)

### Receipt Emission Modes

Receipts support three emission policies configurable at the tenant level:

| Mode | Behavior |
|------|----------|
| `on_request` (default) | Callers opt in per-request via `emit_receipt: true` |
| `always` | Every context/handoff request generates a receipt, overriding `emit_receipt: false` |
| `never` | No receipts are generated, regardless of request flags |

配置来源：[server/schemas/requests.py](https://github.com/smaramwbc/statewave/blob/main/server/schemas/requests.py)

### Receipt Response Fields

When a receipt is successfully written, the response includes:

```python
receipt_id: str | None  # UUID of the written receipt, None if emission failed
receipt_emitted: bool   # True iff receipt was successfully persisted
```

The `receipt_emitted` field distinguishes between three states:

1. **True** — Receipt was successfully written
2. **False with `receipt_id = None`** — Receipt emission was not requested
3. **False with `receipt_id = ...`** — Emission was attempted but failed; the response is still authoritative

资料来源：[server/schemas/responses.py](https://github.com/smaramwbc/statewave/blob/main/server/schemas/responses.py)

### Receipt Emission Workflow

```mermaid
sequenceDiagram
    participant Caller
    participant ContextService
    participant PolicyEngine
    participant ReceiptsService
    participant Database

    Caller->>ContextService: POST /v1/context (emit_receipt=true)
    ContextService->>PolicyEngine: Filter memories by sensitivity labels
    PolicyEngine-->>ContextService: Filtered memories + applied/skipped rules
    
    ContextService->>ContextService: Assemble context bundle
    ContextService->>ReceiptsService: Build receipt body
    ReceiptsService->>Database: Write receipt record
    Database-->>ReceiptsService: receipt_id
    
    ContextService-->>Caller: ContextBundleResponse + receipt_id, receipt_emitted=true
```

The `_maybe_emit_receipt` helper in the context service implements this flow, accepting parameters for memory rows, episode rows, policy decisions, and caller identity.

资料来源：[server/services/context.py](https://github.com/smaramwbc/statewave/blob/main/server/services/context.py)

---

## Policy Engine

### Architecture Overview

The Policy Engine operates on an immutable, content-addressed bundle model. Each policy bundle is:

1. **Content-addressed** — Identified by `bundle_hash` (SHA-256 of YAML content)
2. **Tenant-scoped** — Same YAML can be installed by different tenants as independent rows
3. **Activatable** — Only one bundle per tenant can be active at a time

```mermaid
graph TD
    A[YAML Policy File] --> B[Compute bundle_hash]
    B --> C[PolicyBundleRow]
    C --> D{Tenant A}
    C --> E{Tenant B}
    D --> F[Active Bundle]
    E --> G[Active Bundle]
    
    H[Context Request] --> I[Lookup active bundle]
    I --> F
    F --> J[Apply filters to memories]
    J --> K[Filtered response]
```

资料来源：[server/db/tables.py](https://github.com/smaramwbc/statewave/blob/main/server/db/tables.py)

### PolicyBundleRow Schema

| Column | Type | Description |
|--------|------|-------------|
| `id` | `UUID` | Synthetic primary key (allows multiple tenants to install same bundle) |
| `bundle_hash` | `String(64)` | SHA-256 of YAML content, indexed |
| `yaml_content` | `Text` | Full YAML policy definition |
| `active` | `Boolean` | Whether this is the tenant's active policy |
| `tenant_id` | `String(256)` | Tenant scope (NULL for system-wide) |
| `config` | `JSONB` | Additional configuration |
| `version` | `Integer` | Row version for optimistic locking |
| `created_at` | `DateTime` | Creation timestamp |
| `updated_at` | `DateTime` | Last modification timestamp |

The composite unique index on `(tenant_id, bundle_hash) NULLS NOT DISTINCT` enforces "one bundle per (scope, content)" — two tenants can independently install identical policy YAML.

资料来源：[server/db/tables.py](https://github.com/smaramwbc/statewave/blob/main/server/db/tables.py)

### Policy Modes

The policy engine operates in two modes:

| Mode | Behavior | Use Case |
|------|----------|----------|
| `log_only` (default) | Records what *would* be filtered into receipts without removing memories | Initial deployment, auditing, compliance review |
| `enforce` | Drops denied memories and redacts marked ones from the response | Production enforcement after validating log_only behavior |

```python
policy_mode: Literal["log_only", "enforce"] | None = Field(
    None,
    description=(
        "Sensitivity-label policy enforcement mode (#50). `log_only` "
        "(the default) records what *would* be filtered into receipts "
        "without removing memories from the response. `enforce` drops "
        "denied memories and redacts marked ones. Flip to enforce only "
        "after auditing the log_only receipts for a few days."
    ),
)
```

配置来源：[server/schemas/requests.py](https://github.com/smaramwbc/statewave/blob/main/server/schemas/requests.py)

### Policy Filter Tracking

When a context bundle is assembled, the policy engine records:

- **`filters_applied`** — List of policy rules that resulted in memory exclusions or modifications
- **`filters_skipped`** — List of policy rules that were evaluated but not triggered

This data is written to receipts, enabling operators to:

1. Verify that policies are being evaluated correctly
2. Identify false positives in `enforce` mode before full deployment
3. Generate compliance reports on data access patterns

资料来源：[server/services/context.py](https://github.com/smaramwbc/statewave/blob/main/server/services/context.py)

---

## Integration Points

### Context Assembly (`/v1/context`)

The primary integration point where receipts are emitted and policy is enforced:

```
POST /v1/context
{
  "subject_id": "customer-123",
  "task": "support_response",
  "emit_receipt": true,
  "policy_mode": "enforce",
  ...
}
```

Response includes `receipt_id` and `receipt_emitted` fields.

### Handoff Service (`/v1/handoff`)

The handoff service also supports receipt emission for agent-to-agent transitions:

```python
handoff_notes=handoff_notes,
token_estimate=token_estimate,
active_fact_rows=[row for row in fact_rows if row.status == "active"],
session_episode_rows=current_session_eps[:10],
emit_receipt=emit_receipt,
policy_bundle=active_bundle,
policy_mode=policy_mode,
```

资料来源：[server/services/handoff.py](https://github.com/smaramwbc/statewave/blob/main/server/services/handoff.py)

### Tenant Configuration

Receipts and policy settings can be configured at the tenant level:

| Configuration | Type | Default | Description |
|--------------|------|---------|-------------|
| `receipts` | `Literal["always", "on_request", "never"]` | `on_request` | Global receipt emission policy |
| `receipt_retention_days` | `int` | `0` (forever) | Days to retain receipts before purge (0 = never purge) |
| `policy_mode` | `Literal["log_only", "enforce"]` | `log_only` | Default policy enforcement mode |
| `require_caller_identity` | `bool` | `false` | Require `caller_id`/`caller_type` for context/handoff |

配置来源：[server/schemas/requests.py](https://github.com/smaramwbc/statewave/blob/main/server/schemas/requests.py)

---

## Security Considerations

### Caller Identity

When `require_caller_identity` is enabled, the `/v1/context` and `/v1/handoff` endpoints will:

- Reject anonymous callers (missing both `caller_id` and `caller_type`)
- Record caller identity in receipts for audit purposes

This ensures that every context assembly can be traced to a specific service or user.

### Immutable Receipts

Receipts are append-only by design. Once written, they cannot be modified. This provides:

- Non-repudiation for compliance audits
- Reproducibility of AI decisions
- Historical tracking of policy changes

### Policy Bundle Integrity

The content-addressed nature of policy bundles means:

- Any modification to the YAML content produces a different `bundle_hash`
- The old bundle remains accessible by its hash
- The `bundle_hash` recorded in receipts provides proof of which policy version was applied

---

## Related Documentation

- [API v1 Contract](https://github.com/smaramwbc/statewave-docs/blob/main/api/v1-contract.md)
- [Getting Started Guide](https://github.com/smaramwbc/statewave-docs/blob/main/getting-started.md)
- [Architecture Overview](https://github.com/smaramwbc/statewave-docs/blob/main/architecture/overview.md)

---

<a id='page-multi-tenancy'></a>

## Multi-tenancy & Security

### 相关页面

相关主题：[Governance - Receipts & Policy Engine](#page-receipts-policy)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [server/core/tenant.py](https://github.com/smaramwbc/statewave/blob/main/server/core/tenant.py)
- [server/core/auth.py](https://github.com/smaramwbc/statewave/blob/main/server/core/auth.py)
- [server/core/ratelimit.py](https://github.com/smaramwbc/statewave/blob/main/server/core/ratelimit.py)
- [server/services/webhooks.py](https://github.com/smaramwbc/statewave/blob/main/server/services/webhooks.py)
- [server/api/admin.py](https://github.com/smaramwbc/statewave/blob/main/server/api/admin.py)
- [server/services/ratelimit.py](https://github.com/smaramwbc/statewave/blob/main/server/services/ratelimit.py)
</details>

# Multi-tenancy & Security

## Overview

Statewave implements a multi-tenant architecture with comprehensive security controls for enterprise deployments. The system provides query-scoped data isolation, per-tenant policy enforcement, caller identity validation, and distributed rate limiting—all coordinated through a Postgres-backed shared infrastructure.

## Architecture

```mermaid
graph TD
    subgraph "API Layer"
        A[Incoming Request] --> B[Tenant Resolution]
        B --> C[Auth Middleware]
        C --> D[Rate Limiter]
        D --> E[Policy Engine]
        E --> F[Business Logic]
    end
    
    subgraph "Security Components"
        C -->|API Key| Auth[Auth Service]
        D -->|Distributed| RL[Rate Limit Store]
        E -->|Bundle Hash| PB[Policy Bundles]
        E -->|caller_id/caller_type| CI[Caller Identity]
    end
    
    subgraph "Tenant Isolation"
        B -->|tenant_id| TI[Tenant Isolation Layer]
        TI -->|query scoping| DB[(Postgres)]
        PB -->|per-tenant| TI
    end
```

## Tenant Resolution

### Tenant Header Configuration

Statewave uses a configurable HTTP header for tenant identification. The header name defaults to `X-Tenant-ID` but can be customized via environment variables.

| Environment Variable | Default | Description |
|---------------------|---------|-------------|
| `STATEWAVE_TENANT_HEADER` | `X-Tenant-ID` | Header for multi-tenant isolation |
| `STATEWAVE_REQUIRE_TENANT` | `false` | Reject requests without tenant header |

When `STATEWAVE_REQUIRE_TENANT` is set to `true`, any request missing the tenant header receives a `400 Bad Request` response. When `false`, requests without a tenant header are processed with an anonymous tenant context.

资料来源：[README.md:env-vars]() (configuration documentation)

### Tenant Resolution Flow

```mermaid
sequenceDiagram
    participant C as Client
    participant A as API Server
    participant T as Tenant Resolver
    participant DB as Postgres
    
    C->>A: Request + X-Tenant-ID header
    A->>T: Extract tenant_id
    T->>DB: Lookup tenant config
    DB-->>T: TenantConfigResponse
    T-->>A: Tenant context
    A->>A: Apply tenant-scoped logic
```

## Tenant Configuration API

### Configuration Schema

The `TenantConfigResponse` schema defines the tenant configuration structure:

| Field | Type | Description |
|-------|------|-------------|
| `tenant_id` | `str` | Unique tenant identifier |
| `config` | `dict[str, Any]` | Full configuration document |
| `version` | `int` | Optimistic-concurrency counter |
| `created_at` | `datetime \| None` | First configuration timestamp |
| `updated_at` | `datetime \| None` | Last modification timestamp |

### Configuration Keys

| Key | Type | Description |
|-----|------|-------------|
| `receipts` | `string` | Emission mode: `"always"`, `"never"`, `"on_request"` |
| `receipt_retention_days` | `int` | Days to retain receipts before cleanup |
| `policy_mode` | `string` | `"log_only"` or `"enforce"` |
| `require_caller_identity` | `bool` | Enforce caller_id/caller_type presence |

资料来源：[server/schemas/responses.py:tenant-config-schema]() (response schema definition)

### Admin Endpoints

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/admin/tenants/{id}/config` | Retrieve tenant configuration |
| `PATCH` | `/admin/tenants/{id}/config` | Update tenant configuration |

#### PATCH Request Shape

The PATCH operation performs a merge update with optimistic concurrency control:

```python
# Expected version must match current state
{
    "expected_version": 5,
    "config": {
        "policy_mode": "enforce",
        "receipt_retention_days": 90
    }
}
```

If `expected_version` does not match the current database version, the update fails with a `409 Conflict` response.

资料来源：[server/api/admin.py:config-endpoints]() (admin API implementation)

## Authentication & Authorization

### API Key Authentication

Statewave supports API key authentication via the `Authorization` header:

```http
Authorization: Bearer <api_key>
```

Or via query parameter for specific integration scenarios:

```http
GET /v1/context?api_key=<api_key>
```

### Caller Identity Validation

When `require_caller_identity` is enabled in tenant config, every context and handoff request must include:

| Field | Type | Description |
|-------|------|-------------|
| `caller_id` | `str` | Unique identifier of the calling agent/user |
| `caller_type` | `str` | Type classification (e.g., `"agent"`, `"user"`, `"system"`) |

Requests missing these fields receive a `401 Unauthorized` response:

```json
{
    "detail": "tenant config requires caller_id and caller_type on every context call"
}
```

资料来源：[server/api/handoff.py:identity-validation]() (identity enforcement)

## Rate Limiting

### Rate Limit Strategy

Statewave supports two rate limiting backends:

| Strategy | Backend | Use Case |
|----------|---------|----------|
| `distributed` | Postgres | Multi-instance deployments |
| `memory` | In-process | Single instance / development |

### Configuration

| Environment Variable | Default | Description |
|---------------------|---------|-------------|
| `STATEWAVE_RATE_LIMIT` | `1000` | Requests per window |
| `STATEWAVE_RATE_WINDOW` | `60` | Window duration in seconds |
| `STATEWAVE_RATE_LIMIT_STRATEGY` | `distributed` | Backend strategy selection |

### Current Limitations

> **Note**: Rate limiting is currently keyed by IP address only, not per-tenant or per-API-key. This is a known limitation in v0.8.0.

资料来源：[README.md:rate-limiting-limits]() (documentation of limitations)

## Policy Engine

### Policy Bundle Structure

Policy bundles are declarative YAML/JSON documents that define filtering rules. Each bundle is:

- **Immutable**: Once created, content cannot be modified
- **Content-addressed**: Identified by `bundle_hash` (SHA-256)
- **Per-tenant**: Same bundle content can exist independently per tenant

```yaml
# Example policy bundle
version: 1
rules:
  - id: redact_sensitive
    predicates:
      - kind: label_match
        args:
          label: PII
      - kind: caller_type
        args:
          type: external_agent
    action: redact
```

### Predicate Types

| Predicate | Description |
|-----------|-------------|
| `label_match` | Match memory by sensitivity label |
| `caller_type` | Match by calling agent type |
| `caller_id` | Match by specific caller identifier |

### Actions

| Action | Behavior |
|--------|----------|
| `deny` | Reject the memory from context |
| `redact` | Remove sensitive content, keep metadata |

### Policy Modes

| Mode | Behavior |
|------|----------|
| `log_only` | Record decisions to receipts, no filtering applied |
| `enforce` | Drop denied memories before ranking |

资料来源：[server/db/tables.py:policy-bundle-row]() (database schema)

## Receipts System

### Receipt Emission Logic

Receipts record policy decisions for audit and compliance. The emission decision follows a priority cascade:

```mermaid
flowchart TD
    A[Receipt Request] --> B{Global Kill Switch?}
    B -->|Enabled| E[Emit = False]
    B -->|Disabled| C{Bundle Force Off?}
    C -->|Yes| E
    C -->|No| D{Tenant Mode?}
    D -->|always| F[Emit = True]
    D -->|never| E
    D -->|on_request| G{Request Flag?}
    G -->|True| F
    G -->|False| E
```

### Kill Switch

Global environment variable disables all receipt emission:

```bash
STATEWAVE_RECEIPTS_DISABLED=true  # or "1", "yes"
```

### Receipt Data Model

| Field | Description |
|-------|-------------|
| `id` | ULID identifier |
| `tenant_id` | Associated tenant |
| `policy_bundle_hash` | Hash of applied policy bundle |
| `policy_mode` | Mode at time of decision |
| `filters_applied` | List of applied filter rules |
| `filters_skipped` | List of bypassed filter rules |
| `caller_id` | Identity of requesting agent |
| `caller_type` | Type of requesting agent |

资料来源：[server/services/receipts.py:emission-decision]() (emission logic)

## Multi-Tenant Data Isolation

### Query-Scoped Isolation

All database queries include tenant_id in their WHERE clauses. This is enforced at the service layer:

```python
# Example isolation pattern
query = select(MemoryRow).where(
    MemoryRow.tenant_id == tenant_id,
    MemoryRow.subject_id == subject_id
)
```

### Composite Unique Index

Policy bundles use a composite unique index to enforce:

```sql
UNIQUE (tenant_id, bundle_hash) NULLS NOT DISTINCT
```

This allows multiple tenants to install identical policy content while maintaining row-level separation.

资料来源：[server/db/tables.py:composite-index]() (index definition)

## Security Best Practices

### Recommended Configuration

For production deployments:

```yaml
# Kubernetes/Helm values
auth:
  apiKey: <use-external-secret>
  
database:
  url: <use-external-secret>
  existingSecret: statewave-db
  existingSecretKey: STATEWAVE_DATABASE_URL

# Tenant configuration
policy_mode: enforce
require_caller_identity: true
receipts: always
receipt_retention_days: 90
```

### Secret Management

The Helm chart supports two patterns:

1. **Inline secrets**: Set via `--set` for development
2. **External secrets**: Reference existing secrets (recommended for production)

| Secret | Purpose |
|--------|---------|
| `statewave-db` | Database connection URL |
| `statewave-llm` | LLM provider API key |
| `statewave-auth` | API authentication key |

资料来源：[helm/statewave/README.md:secret-management]() (Helm documentation)

## Current Limitations

| Feature | Status | Notes |
|---------|--------|-------|
| Rate limiting per-IP | ✅ Shipped | Distributed but IP-only |
| Rate limiting per-tenant | 🔜 Planned | Not yet implemented |
| Rate limiting per-API-key | 🔜 Planned | Not yet implemented |
| Postgres RLS | 🔜 Planned | App-layer isolation only |
| Multi-tenant policy bundles | ✅ v0.8 | Content-hashed, per-tenant |
| Caller identity validation | ✅ v0.8 | Configurable per tenant |

资料来源：[README.md:current-limitations]() (version 0.8.0 status)

---

---

## Doramagic 踩坑日志

项目：smaramwbc/statewave

摘要：发现 6 个潜在踩坑项，其中 0 个为 high/blocking；最高优先级：能力坑 - 能力判断依赖假设。

## 1. 能力坑 · 能力判断依赖假设

- 严重度：medium
- 证据强度：source_linked
- 发现：README/documentation is current enough for a first validation pass.
- 对用户的影响：假设不成立时，用户拿不到承诺的能力。
- 建议检查：将假设转成下游验证清单。
- 防护动作：假设必须转成验证项；没有验证结果前不能写成事实。
- 证据：capability.assumptions | github_repo:1219923941 | https://github.com/smaramwbc/statewave | README/documentation is current enough for a first validation pass.

## 2. 维护坑 · 维护活跃度未知

- 严重度：medium
- 证据强度：source_linked
- 发现：未记录 last_activity_observed。
- 对用户的影响：新项目、停更项目和活跃项目会被混在一起，推荐信任度下降。
- 建议检查：补 GitHub 最近 commit、release、issue/PR 响应信号。
- 防护动作：维护活跃度未知时，推荐强度不能标为高信任。
- 证据：evidence.maintainer_signals | github_repo:1219923941 | https://github.com/smaramwbc/statewave | last_activity_observed missing

## 3. 安全/权限坑 · 下游验证发现风险项

- 严重度：medium
- 证据强度：source_linked
- 发现：no_demo
- 对用户的影响：下游已经要求复核，不能在页面中弱化。
- 建议检查：进入安全/权限治理复核队列。
- 防护动作：下游风险存在时必须保持 review/recommendation 降级。
- 证据：downstream_validation.risk_items | github_repo:1219923941 | https://github.com/smaramwbc/statewave | no_demo; severity=medium

## 4. 安全/权限坑 · 存在评分风险

- 严重度：medium
- 证据强度：source_linked
- 发现：no_demo
- 对用户的影响：风险会影响是否适合普通用户安装。
- 建议检查：把风险写入边界卡，并确认是否需要人工复核。
- 防护动作：评分风险必须进入边界卡，不能只作为内部分数。
- 证据：risks.scoring_risks | github_repo:1219923941 | https://github.com/smaramwbc/statewave | no_demo; severity=medium

## 5. 维护坑 · issue/PR 响应质量未知

- 严重度：low
- 证据强度：source_linked
- 发现：issue_or_pr_quality=unknown。
- 对用户的影响：用户无法判断遇到问题后是否有人维护。
- 建议检查：抽样最近 issue/PR，判断是否长期无人处理。
- 防护动作：issue/PR 响应未知时，必须提示维护风险。
- 证据：evidence.maintainer_signals | github_repo:1219923941 | https://github.com/smaramwbc/statewave | issue_or_pr_quality=unknown

## 6. 维护坑 · 发布节奏不明确

- 严重度：low
- 证据强度：source_linked
- 发现：release_recency=unknown。
- 对用户的影响：安装命令和文档可能落后于代码，用户踩坑概率升高。
- 建议检查：确认最近 release/tag 和 README 安装命令是否一致。
- 防护动作：发布节奏未知或过期时，安装说明必须标注可能漂移。
- 证据：evidence.maintainer_signals | github_repo:1219923941 | https://github.com/smaramwbc/statewave | release_recency=unknown

<!-- canonical_name: smaramwbc/statewave; human_manual_source: deepwiki_human_wiki -->
