<div align="center">

# 🐍 PyClaw

**A production-grade Python AI Agent framework with persistent memory, hooks-driven architecture, and compute-storage separation.**

[English](./README.md) · [中文文档](./README_CN.md) · [📚 WeChat: Time留痕 公众号合集 →](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzY5ODI5NzUwNA==&action=getalbum&album_id=4503553062812516353)

[![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat-square)](./LICENSE)
[![Python](https://img.shields.io/badge/python-3.12%2B-3776ab.svg?style=flat-square&logo=python&logoColor=white)](https://www.python.org/)
[![Tests](https://img.shields.io/badge/tests-2168%20passed-brightgreen.svg?style=flat-square)]()
[![Memory](https://img.shields.io/badge/memory-4--layer%20%E2%9C%93-blueviolet.svg?style=flat-square)]()
[![FTS5](https://img.shields.io/badge/FTS5-jieba%20tokenizer-orange.svg?style=flat-square)]()
[![Channels](https://img.shields.io/badge/channels-Feishu%20%2B%20Web-blue.svg?style=flat-square)]()
[![OpenAI Compat](https://img.shields.io/badge/API-OpenAI%20compatible-412991.svg?style=flat-square&logo=openai&logoColor=white)]()

</div>

<br clear="all"/>

![PyClaw Web Channel](./docs/assets/web-channel-chat.png?v=2)

---

## 🎯 Positioning

**Team-aware AI agent with institutional memory that compounds — embedded in your team's communication platform.**

Built for general-purpose use — daily assistance, complex multi-step tasks, software engineering — with four traits no other open-source agent ships well together:

- **Persistent multi-layer memory** that survives sessions, accumulates per-team knowledge, and grades from working memory all the way to vector archives
- **Self-evolution without fine-tuning** — automatically extracts reusable SOPs from successful sessions, ages out stale ones (30d / 90d), agent can actively forget outdated procedures, and high-frequency SOPs graduate into reusable skills. The agent gets *measurably better* at your team's recurring tasks over weeks of use, with no model retraining
- **Multi-channel as first-class** — Feishu (Lark), Web, and (planned) TUI / VSCode extension share the same agent core; non-terminal-native teammates can interact directly
- **Hooks system for enterprise workflow integration** — compliance, audit, approval gates, custom tooling all plug in without forking core

The competitive landscape has strong general-purpose coding agents (Claude Code, Cursor, OpenCode, Aider) and strong general-purpose chat agents (ChatGPT, Claude). Most of them treat each session as a fresh start. PyClaw's defensible angle is the intersection: **a coding-capable agent that lives where your team already talks, remembers your codebase conventions and architecture decisions across weeks, learns the team's recurring playbooks on its own, and respects your enterprise approval flow.** Daily assistance, research, code, ops — same agent, same memory, same audit trail, and it compounds with use.

> Strategic context: see [planning roadmap](./DailyWork/planning/ROADMAP.md) (private) and the [strategic discussion record](./DailyWork/planning/exploration/2026-05-15-strategic-roadmap-coding-agent.md) (private).

---

## ✨ Why PyClaw?

OpenClaw is a powerful multi-channel AI assistant — but its TypeScript monolith (17,000+ files) tightly couples compute and storage, and lacks production-grade memory. PyClaw rebuilds it from scratch in Python with a **memory-first, hooks-driven, horizontally scalable** architecture:

- 🧠 **4-Layer Memory System** — L1 Redis hot index → L2 facts → L3 procedures → L4 vector archives. Production-ready, fully integrated into the agent loop.
- 🔄 **Self-Evolution** — Auto-extracts SOPs from sessions, curates lifecycle (30d stale / 90d archive), agent can actively forget outdated procedures. No fine-tuning needed.
- 🪝 **Hooks-Driven Architecture** — Memory injection, working memory, nudges, tool approval — all built as pluggable hooks. Add your own without touching core.
- ☁️ **Compute-Storage Separation** — Stateless workers behind any load balancer. Sessions in Redis, memory in SQLite/Redis, embeddings via litellm.
- 🌐 **Multi-Channel** — Feishu (Lark) WebSocket cluster + Web channel with React SPA + OpenAI-compatible `/v1/chat/completions` SSE.
- 🚦 **Session Affinity Gateway** — Active-active multi-worker scaling via Redis-backed affinity routing + PubSub forwarding. Same session always handled by the same worker, regardless of how Feishu/LB dispatches messages. Failover via TTL + PUBLISH-subscriber-count detection.
- 🇨🇳 **Built for Chinese** — FTS5 + jieba tokenizer for Chinese full-text memory search. Stop words. Auto-migration from trigram.
- 🎯 **Prompt Budget Engineering** — Frozen prefix (cacheable) + per-turn suffix (dynamic). Priority-based truncation. 90%+ prompt cache hit rate.

---

## 🚀 Quick Start

> **Going beyond 2-minute setup?** Jump to [§ Configuration & Deployment](#️-configuration--deployment) for full guides on local dev, single-instance Docker, 3-worker active-active, multi-user isolation, sandboxing, MCP servers, and tool approval.

### As a Feishu (Lark) Bot — 2 minutes

```bash
git clone https://github.com/Timeflys2018/pyclaw.git && cd pyclaw
python3.12 -m venv .venv && .venv/bin/pip install -e ".[dev]"

# Configure Feishu app credentials in configs/pyclaw.json
.venv/bin/python -c "import json,pathlib; pathlib.Path('configs/pyclaw.json').write_text(json.dumps({
  'channels': {'feishu': {'enabled': True, 'appId': 'cli_...', 'appSecret': '...'}}
}, indent=2))"

./scripts/start.sh
```

> 📖 **More**: full `pyclaw.json` schema in the [configuration reference](./docs/en/configuration.md), or copy [`configs/pyclaw.example.json`](./configs/pyclaw.example.json). Production single-instance deploy: see [deployment guide § Single Docker](./docs/en/deployment.md).

### As a Web Agent — 2 minutes

```bash
./scripts/start.sh                # Starts backend + auto-builds React SPA
open http://localhost:8000        # Login (default: admin / changeme)
```

Web channel ships with: streaming chat with inline execution trace (tool calls + memory hits + token usage), multimodal input (paste / drop / attach images), `⌘K` command palette, keyboard shortcuts, session rename + delete, dual-theme (light/dark), tool approval modal, OpenAI-compatible API for third-party clients.

> 📖 **More**: tune the 3-tier permission model (`read-only` / `approval` / `yolo`) in the [permissions guide](./docs/en/permissions.md). Add MCP servers (GitHub, filesystem, etc.) via the [MCP guide](./docs/en/mcp.md). Multi-user team deploy with per-user tier isolation: see [multi-user deployment](./docs/en/multi-user-deployment.md).

### As a Library

```python
from pyclaw.core.agent.factory import build_agent_runner
from pyclaw.infra.settings import load_settings

settings = load_settings("configs/pyclaw.json")
runner = build_agent_runner(settings)

async for event in runner.run("Help me debug this Python error..."):
    print(event)
```

> 📖 **More**: settings types, hooks, and the agent loop API live in [architecture decisions](./docs/en/architecture-decisions.md) (D1–D26) and [context engine](./docs/en/context-engine.md).

---

## 🧠 Memory System (Headline Feature)

The memory system is a **4-layer pipeline** integrated into every prompt:

```mermaid
flowchart LR
    User["User Prompt"] --> L1["L1: Redis<br/>Working Memory<br/>(per session)"]
    User --> L2["L2: SQLite + FTS5<br/>Facts<br/>(jieba tokenizer)"]
    User --> L3["L3: SQLite + FTS5<br/>Procedures/SOPs"]
    User --> L4["L4: SQLite + sqlite-vec<br/>Session Archives"]

    L1 -.snapshot.-> Prompt["Frozen Prefix"]
    L2 -.facts ≤3.-> Dynamic["Dynamic Zone"]
    L3 -.procedures ≤2.-> Dynamic
    L4 -.semantic search.-> Dynamic

    Prompt --> Agent[Agent Loop]
    Dynamic --> Agent

    style L1 fill:#fff3e0,stroke:#e65100
    style L2 fill:#e8f5e9,stroke:#2e7d32
    style L3 fill:#e8f5e9,stroke:#2e7d32
    style L4 fill:#e3f2fd,stroke:#1565c0
```

**Hooks that drive it** (no LLM-side changes needed):

| Hook | What it does |
|------|--------------|
| `WorkingMemoryHook` | Injects `<working_memory>` XML into every turn (per-session Redis KV) |
| `MemoryNudgeHook` | Every 10 turns, nudges agent: "Consider using `memorize`." Counter resets on use |
| `archive_session_background` | On `/new`, archives old session → L4 with vector embedding (non-blocking) |
| `ContextEngine.assemble` | Searches L2/L3 by user prompt, injects top-K facts + procedures |

**Tools the agent calls itself**:

- `memorize` — Persist to L2 (facts) or L3 (procedures). "No execution, no memory" guard.
- `forget` — Archive outdated/failed SOPs. Agent-initiated lifecycle management.
- `update_working_memory` — Per-session scratchpad (1024 char cap, 7-day TTL, FIFO eviction).
- `skill_view` — Progressive disclosure: load full SKILL.md content on demand.

---

## 🔄 Self-Evolution (New!)

PyClaw's agent **improves itself** over time — no fine-tuning, no retraining:

```mermaid
flowchart LR
    subgraph Extract["1. Extract"]
        A[Task] --> B[Tracker Hook]
        B --> C{Session end?}
        C -->|threshold met| D[LLM extract]
        D --> E[Dedup + Write L3]
    end

    subgraph Curate["2. Curate"]
        E --> F[Search hit<br/>bump count]
        F --> G{90d unused?}
        G -->|yes| H[archived]
        I[forget tool] --> H
    end

    subgraph Graduate["3. Graduate"]
        F --> J{count ≥ 5<br/>age ≥ 7d?}
        J -->|yes| K[SKILL.md]
        K --> L[skill_view]
    end

    style Extract fill:#e8f5e9,stroke:#2e7d32
    style Curate fill:#fff3e0,stroke:#e65100
    style Graduate fill:#e3f2fd,stroke:#1565c0
```

**The timeline:**

| Day | What happens |
|-----|-------------|
| Day 1 | Agent follows instructions normally |
| Day 7 | Extracts reusable SOPs from successful sessions |
| Day 30 | Unused SOPs flagged as stale (still active, CLI visible) |
| Day 60 | Agent actively forgets outdated SOPs via `forget` tool |
| Day 90 | Curator auto-archives remaining unused SOPs |
| Day 90+ | High-frequency SOPs (use_count ≥ N + age ≥ M days) auto-graduate to SKILL.md via Curator (template or LLM-enrich mode) |

**Key design decisions:**
- **Strict rejection bias** — better to miss a valid SOP than learn a bad one
- **Stale is computed, not stored** — active SOPs remain searchable; "stale" is a CLI view, not a DB state
- **Deterministic + Agent-driven** — Curator handles time decay; `forget` tool handles quality judgment
- **Distributed-safe** — Redis SETNX lock ensures only one Curator instance runs across workers

---

## 🏛 Architecture

```mermaid
graph TB
    subgraph Channels["🌐 Channels"]
        CH[Feishu WebSocket · Web WS · OpenAI SSE]
    end

    subgraph Compute["☁️ Compute Layer — Stateless Workers"]
        direction TB
        Runner["Agent Runner · 770-line loop<br/>Frozen Prefix · Per-Turn Suffix · Prompt Budget"]
        Tools["Tools: bash · read · write · edit · grep · glob · web_fetch<br/>memorize · forget · update_working_memory · skill_view"]
        Hooks["Hooks: WorkingMemory · MemoryNudge · ToolApproval · SopTracker"]
        CE["Context Engine: assemble + memory search + compact"]
        Infra["Infra: TaskManager · Curator · Skill Graduation · Settings"]
    end

    subgraph Storage["💾 Storage Layer"]
        direction TB
        Redis[("Redis<br/>Sessions · Locks · L1 Index · Working Memory")]
        Memory[("SQLite + FTS5 + jieba<br/>L2 Facts · L3 Procedures")]
        Vec[("sqlite-vec<br/>L4 Session Archives")]
        Embed["Embedding API · litellm"]
    end

    CH --> Runner
    Runner --> Tools
    Runner --> Hooks
    Runner --> CE
    Hooks --> Redis
    CE --> Memory
    CE --> Vec
    CE --> Embed
    Infra --> Redis

    style Channels fill:#e3f2fd,stroke:#1565c0
    style Compute fill:#f3e5f5,stroke:#6a1b9a
    style Storage fill:#e8f5e9,stroke:#2e7d32
```

---

## 📊 Current Status

| Layer | Status | Highlights |
|-------|--------|-----------|
| **Agent Core** | ✅ | 770-line single loop, 11 builtin tools (Tier 2: `web_fetch` / `grep` / `glob`), hook system, 5-file compaction subsystem |
| **Memory System** | ✅ | 4-layer (L1/L2/L3/L4), FTS5 + jieba, sqlite-vec, auto-migration from trigram |
| **Context Engine** | ✅ | Frozen/per-turn split, memory search, L1 snapshot, prompt budget |
| **Session Store** | ✅ | Redis (production) + InMemory (dev), SessionKey/SessionId rotation, DAG tree |
| **Feishu Channel** | ✅ | WebSocket cluster (50 workers), CardKit streaming, slash commands |
| **Web Channel** | ✅ | React 19 SPA · Linear/Cursor visual · execution trace · multimodal · ⌘K palette · keyboard shortcuts · session CRUD · OpenAI-compat SSE · JWT auth · **tool approval modal + 3-tier permissions (read-only / approval / yolo)** |
| **Tool Approval** | ✅ | End-to-end: WebToolApprovalHook + Feishu CardKit interactive card (originator-only authorization) · per-turn tier override · structured JSON audit log. See [permissions guide](./docs/en/permissions.md) |
| **MCP Client** | ✅ | stdio servers · `{server}:{tool}` namespace · trust-tier mapping with operator overrides (de-escalation only) · `/mcp list / restart / logs` · non-blocking startup with `/health` advisory · `{env:VAR}` secret placeholders. See [MCP guide](./docs/en/mcp.md) |
| **Skill Hub** | ✅ | ClawHub-compatible, progressive disclosure, **7-layer discovery (incl. `.claude/skills/` cross-ecosystem read)**, `pyclaw-skill` CLI. See [skill-hub-compatibility.md](./docs/en/skill-hub-compatibility.md#discovery-order-high--low-priority) |
| **Builtin Tier 2 tools** | ✅ | In-process `web_fetch` (httpx + markdownify), `grep` (Python `re`, sandbox-friendly), `glob` (mtime-sorted), optional `fetch_mcp_resource`. All `tool_class="read"` so they work under `tier=read-only` where `bash` is denied. See [builtin-tools.md](./docs/en/builtin-tools.md) |
| **Prompt Engineering** | ✅ | `PromptBudgetConfig`, frozen prefix caching, priority truncation |
| **TaskManager** | ✅ | Centralized async lifecycle, K8s-grade graceful shutdown |
| **Self-Evolution** | ✅ | SOP extraction + Curator lifecycle (30d/90d) + ForgetTool + Skill Graduation (high-frequency SOPs → SKILL.md) + CLI audit |
| **Session Affinity Gateway** | ✅ | Active-active multi-worker scaling, Redis affinity + PubSub forwarding, failover via PUBLISH-0 fallback |
| **Sub-Agent System** | ✅ | 5-layer discovery, `.md + frontmatter` manifest, isolated context window, tier/sandbox/memory inheritance, depth-1 limit, `/agents` slash command. License-clean (zero bundled personas). See [subagents guide](./docs/en/subagents.md). |
| **Dreaming Engine** | 🔲 | Planned: Light/Deep/REM memory consolidation |

**Test stats:** 2168 unit/integration tests + 10 real-LLM E2E tests · ~12K lines Python · 110 source files

---

## 🎬 Feature Highlights

### 4-Layer Memory + Chinese FTS5

```python
# L2/L3 search hits → injected as <facts> / <procedures> XML in dynamic zone
# All four layers are searched per turn, results blended by priority

# Example: Chinese query just works
agent.run("帮我看一下飞书 streaming 模块的 token 限流策略")
# → FTS5 matches "飞书"+"streaming"+"token"+"限流" via jieba.cut_for_search
# → Top procedures injected into prompt
```

### Hooks-Driven Memory Pipeline

```python
class MyCustomHook(AgentHook):
    async def before_prompt_build(self, ctx):
        ctx.append_dynamic("<custom>...injected...</custom>")
    async def after_response(self, ctx, response):
        # Auto-extract facts after every agent reply
        ...

agent.hooks.register(MyCustomHook())
```

### Frozen / Per-Turn Prompt Architecture

```mermaid
graph LR
    subgraph Frozen["❄️ Frozen Prefix (cached, 90%+ hit)"]
        F[identity · tools · skills · L1 snapshot]
    end
    subgraph Suffix["🔄 Per-Turn Suffix"]
        S[runtime · working_memory · nudge]
    end
    subgraph Dynamic["🔍 Dynamic Zone"]
        D[facts · procedures with entry_id]
    end

    Frozen ~~~ Suffix ~~~ Dynamic

    style Frozen fill:#e3f2fd,stroke:#1565c0
    style Suffix fill:#fff3e0,stroke:#e65100
    style Dynamic fill:#e8f5e9,stroke:#2e7d32
```

### OpenAI-Compatible API

```bash
curl http://localhost:8000/v1/chat/completions \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"model":"pyclaw","messages":[{"role":"user","content":"hi"}],"stream":true}'
```

### Multi-Instance Production Deploy

```yaml
# docker-compose.yml
services:
  pyclaw:
    deploy: { replicas: 3 }       # 3 active-active workers
    environment:
      PYCLAW_AFFINITY_ENABLED: "true"
  redis:
    image: redis:7-alpine         # Shared state + affinity registry
  nginx:
    image: nginx:alpine           # ip_hash + reverse proxy
    volumes: [./deploy/nginx.conf:/etc/nginx/conf.d/default.conf]
    ports: ["80:80"]
```

**Two-layer stickiness for resilience**:
- **Layer 1 — Nginx `ip_hash`**: Routes same client IP to same worker (reduces forwarding overhead)
- **Layer 2 — Session Affinity Gateway**: Redis-backed `session_key → worker_id` mapping ensures the same session is always handled by the same worker, regardless of how Nginx (or Feishu cluster mode) dispatches messages. Cross-worker forwarding via Redis PubSub when needed; failover via TTL expiry + `force_claim` on PUBLISH-0.

**Local dev with reverse proxy** (single entry point at `localhost:9000` → 3 workers on `8000/8001/8002`):

```bash
make worker1     # terminal 1: PORT=8000
make worker2     # terminal 2: PORT=8001
make worker3     # terminal 3: PORT=8002
make nginx-start # reverse proxy on :9000
make affinity-status   # snapshot Redis state (anytime)
```

See [`make help`](./Makefile) for all dev shortcuts and [`reports/affinity-gateway-smoke-2026-05-15.md`](./reports/affinity-gateway-smoke-2026-05-15.md) for the full smoke test report.

---

## 📚 Deep Dives (WeChat Articles)

> 📖 **[完整文章合集 (Full WeChat Article Collection) →](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzY5ODI5NzUwNA==&action=getalbum&album_id=4503553062812516353)**

| # | Title | Topic |
|---|-------|-------|
| A1 | [从 TypeScript 单体到存算分离](https://mp.weixin.qq.com/s/p4AlkEqj1hBN1MdVOjz9BQ) | Why rewrite OpenClaw — three principles |
| A2 | [从 6000 行包装到 645 行单循环](https://mp.weixin.qq.com/s/sGLHdPsMD1vj8CfUTd6PdQ) | Six-framework Agent Core comparison (Claude Code / OpenClaw / OpenCode / DeerFlow / GenericAgent / Hermes) |
| D0 | [AI Agent 记忆系统的四种流派](https://mp.weixin.qq.com/s/1ldmhldoAhq25w-Ov0WhgQ) | Memory schools: Karpathy / 火山 / Shopify / YC |
| D1 | [你的 AI Agent 为什么总是"失忆"？](https://mp.weixin.qq.com/s/f_hUmwMpTFEPqstC7fBOww) | The 4-layer memory architecture design |
| D2 | [给 AI Agent 的记忆系统通上电](https://mp.weixin.qq.com/s/T15stlOpvfF1Jd5sQJ4B_g) | Memory system end-to-end: tool design + hooks + APSW/jieba FTS5 fix |
| E1 | [给 Agent 加一个"心脏起搏器"：TaskManager 设计](https://mp.weixin.qq.com/s/1q67jEmQzvFJ8Dd6Tq_Ujg) | Async task lifecycle for agents |
| E11 | [Slash 命令系统：4 层分工驯服 21 个命令](https://mp.weixin.qq.com/s?__biz=MzY5ODI5NzUwNA==&mid=2247483884&idx=1&sn=98ef67ae00eb9c9fedcf78b727e5ad2c&scene=21#wechat_redirect) | CommandRegistry + ProtocolOp + Hook 多层分工 |
| E12 | [Session Affinity Gateway: N worker 水平扩展](https://mp.weixin.qq.com/s?__biz=MzY5ODI5NzUwNA==&mid=2247483904&idx=1&sn=e0ab6d023825f5ea48510bbda74e4ddc&scene=21#wechat_redirect) | Affinity 双层 stickiness + (N-1)/N forward |
| E17 | [权限系统：4 层 tier × per-user × per-MCP-server](https://mp.weixin.qq.com/s?__biz=MzY5ODI5NzUwNA==&mid=2247484004&idx=1&sn=afa215cd98ebaf7e5c94a34fa4e0b8d8&scene=21#wechat_redirect) | Tool approval system 四阶段演化 |
| E18 | [运行时沙箱：基于 Anthropic srt 的系统实现](https://mp.weixin.qq.com/s?__biz=MzY5ODI5NzUwNA==&mid=2247484020&idx=1&sn=65972af44e09dcd6b16577f0c08d111e&scene=21#wechat_redirect) | 8 条架构不变量 + SandboxPolicy Protocol 三件套 |
| E21 | [Sub-Agent 系统的 8 条架构不变量](https://mp.weixin.qq.com/s?__biz=MzY5ODI5NzUwNA==&mid=2247484029&idx=1&sn=40a0b8cadc3ae3c02be44a6a4b0575af&scene=21#wechat_redirect) | Markdown manifest → isolated executor 全景 |
| F13 | [stub-and-revert 测试方法论](https://mp.weixin.qq.com/s?__biz=MzY5ODI5NzUwNA==&mid=2247484041&idx=1&sn=4b5d9b8d0af38197bacd434f143d9743&scene=21#wechat_redirect) | unit + e2e 全绿不代表无错 |
| B1 | [四种 Agent 架构路径：OpenClaw / Hermes / OpenHuman / PyClaw](https://mp.weixin.qq.com/s?__biz=MzY5ODI5NzUwNA==&mid=2247484047&idx=1&sn=8b15ad35cd6d7465550d165901e084f1&scene=21#wechat_redirect) | 4-way 对比 + 5 个借鉴招式 + 反向校准 |

Series codes: **A** (project) · **B** (competitive) · **C** (context) · **D** (memory + evolution) · **E** (architecture + safety) · **F** (methodology)

---

## ⚙️ Configuration & Deployment

PyClaw is configured via a single `pyclaw.json` discovered in `./pyclaw.json`, `configs/pyclaw.json`, or `~/.openclaw/pyclaw.json`. Five common scenarios are documented in the configuration reference: local dev, single-instance production, multi-instance active-active, Feishu bot, memory + self-evolution.

**Setup & operations:**

- **[Configuration reference (EN)](./docs/en/configuration.md)** · **[配置参考 (中文)](./docs/zh/configuration.md)** — every Settings field, env-var override map, scenario-driven examples (5 scenarios)
- **[Deployment guide (EN)](./docs/en/deployment.md)** · **[部署指南 (中文)](./docs/zh/deployment.md)** — local dev, single Docker, 3-worker active-active with [`deploy/docker-compose.multi.yml`](./deploy/docker-compose.multi.yml), no-Docker `make worker[1-3]`
- **[Multi-user deployment (EN)](./docs/en/multi-user-deployment.md)** · **[多用户部署 (中文)](./docs/zh/multi-user-deployment.md)** — `UserProfile` schema, per-user default tier, role-based permissions (admin / member), `/admin user` slash commands
- **[`configs/pyclaw.example.json`](./configs/pyclaw.example.json)** — complete runnable template

**Security & integrations:**

- **[Permissions & tool approval (EN)](./docs/en/permissions.md)** · **[权限与工具审批 (中文)](./docs/zh/permissions.md)** — three permission tiers (`read-only` / `approval` / `yolo`), per-channel UX (Web modal vs Feishu CardKit), audit log schema
- **[Sandbox (EN)](./docs/en/sandbox.md)** · **[沙箱 (中文)](./docs/zh/sandbox.md)** — `@anthropic-ai/sandbox-runtime` (srt) integration: macOS Seatbelt / Linux bubblewrap+seccomp, `BashTool` and MCP subprocess isolation, 8-invariant manifest, `production_require_sandbox` flag
- **[MCP servers (EN)](./docs/en/mcp.md)** · **[MCP 服务器 (中文)](./docs/zh/mcp.md)** — Anthropic Model Context Protocol integration: stdio server config, `{env:VAR}` secrets, per-server `forced_tier` (de-escalation only), `/mcp list/restart/logs`, non-blocking startup, security warnings

---

## 🛠 CLI Tools

```bash
# Skill management
pyclaw-skill list                    # Discovered skills
pyclaw-skill search github           # Search ClawHub marketplace
pyclaw-skill install github          # Install from ClawHub
pyclaw-skill check                   # Eligibility check (bins/env/OS)

# SOP lifecycle (Curator)
pyclaw-skill curator list --auto     # Active auto-extracted SOPs
pyclaw-skill curator list --stale    # SOPs unused for 30+ days
pyclaw-skill curator list --archived # Archived SOPs (with reason)
pyclaw-skill curator restore <id>    # Restore an archived SOP
pyclaw-skill curator graduate --preview  # Preview graduation candidates
pyclaw-skill curator graduate            # Execute graduation
pyclaw-skill curator graduate --id <id>  # Force-graduate specific SOP

# Live memory inspection
.venv/bin/python scripts/verify_memory_live.py   # Real-time L1/L2/L3/L4 watcher
```

---

## 🧪 Testing

```bash
# Unit + integration (no external deps)
.venv/bin/pytest tests/ --ignore=tests/e2e

# With real Redis
PYCLAW_TEST_REDIS_HOST=localhost .venv/bin/pytest tests/integration/

# Real-LLM E2E
PYCLAW_LLM_API_KEY=sk-... .venv/bin/pytest tests/e2e/
```

1047 unit/integration tests · 10 E2E tests · ~11K LOC across 105 source files.

---

## 📁 Project Structure

```
src/pyclaw/
├── core/                     # Compute layer (stateless)
│   ├── agent/
│   │   ├── runner.py         # Single 770-line agent loop
│   │   ├── system_prompt.py  # Frozen + per-turn builders
│   │   ├── tools/            # bash, read, write, edit, grep, glob, web_fetch, memorize, forget, update_working_memory, skill_view (+ fetch_mcp_resource opt-in)
│   │   ├── hooks/            # WorkingMemoryHook, MemoryNudgeHook, SopTrackerHook
│   │   ├── compaction/       # 5-file subsystem (planning, dedup, hardening, checkpoint, reasons)
│   │   └── factory.py        # Auto-wires memory tools + hooks
│   ├── context_engine.py     # Bootstrap + memory search + assemble
│   ├── curator.py            # Background SOP lifecycle (scan → stale → archive)
│   ├── sop_extraction.py     # LLM-based SOP extraction from sessions
│   ├── memory_archive.py     # Background L4 archival on /new
│   └── hooks.py              # AgentHook / ToolApprovalHook / SkillProvider Protocols
├── storage/
│   ├── memory/               # 4-Layer memory (composite, sqlite, redis_index, jieba_tokenizer, embedding)
│   ├── session/              # Redis + InMemory session stores
│   ├── workspace/            # File + Redis workspace stores
│   └── lock/                 # Redis distributed lock (SET NX PX + Lua CAS)
├── channels/
│   ├── feishu/               # WS receiver, CardKit streaming, slash commands
│   ├── web/                  # WebSocket + REST + OpenAI SSE + React SPA + admin
│   └── session_router.py     # SessionKey → SessionId routing
├── skills/                   # Skill Hub (parser, discovery, eligibility, prompt, clawhub_client, installer)
├── infra/
│   ├── task_manager.py       # Centralized async lifecycle (spawn/cancel/drain)
│   ├── settings.py           # MemorySettings, EmbeddingSettings, PromptBudgetConfig
│   └── redis_client.py
├── cli/skills.py             # pyclaw-skill CLI
└── app.py                    # FastAPI entry + lifespan
```

---

## 🛡 Security & Isolation

PyClaw's current isolation model is **single-tenant or trusted-team** — session data, Redis keys, Feishu workspaces, and memory stores are isolated per user, but there is no tenancy boundary between teams sharing one deployment. Suitable for: a team running its own instance, or trusted internal users on a shared instance. Web channel is for trusted users (Tool Approval Hook gates dangerous operations). Multi-tenant SaaS deployment requires the upgrade path documented below.

See [D26: User Isolation Model](./docs/en/architecture-decisions.md#d26-user-isolation-model--personal-assistant-not-multi-tenant-saas) for full isolation boundaries and multi-tenant upgrade path.

---

## 📖 Documentation

**Getting started & operations**

- [Configuration reference](./docs/en/configuration.md) — every Settings field, scenario-driven
- [Deployment guide](./docs/en/deployment.md) — local dev / single Docker / multi-instance active-active

**Architecture & design**

- [Architecture Decisions (D1–D26)](./docs/en/architecture-decisions.md) — all design choices and rationale
- [Session System Design](./docs/en/session-design.md) — SessionKey/SessionId, commands, idle reset
- [Context Engine](./docs/en/context-engine.md) — assemble/ingest/compact Protocol
- [Compaction Guide](./docs/en/compaction-guide.md) — multi-stage context summarization
- [Timeouts & Abort](./docs/en/timeouts-and-abort.md) — run/idle/tool timeout design
- [Builtin Tools](./docs/en/builtin-tools.md) — 11 builtin tools, Tier 2 web_fetch / grep / glob, sandbox-bypass contract
- [Skill Hub Compatibility](./docs/en/skill-hub-compatibility.md) — ClawHub integration + 7-layer cross-ecosystem discovery

Chinese docs: [docs/zh/](./docs/zh/)

---

## 🗺 Roadmap

- ✅ Memory Store — 4-layer SQLite-vec + FTS5 + jieba
- ✅ Web Channel — multiplexed WebSocket, OpenAI-compat SSE, React SPA
- ✅ Skill Hub — ClawHub SKILL.md parsing, progressive disclosure
- ✅ TaskManager — centralized async task lifecycle
- ✅ Self-Evolution — SOP extraction + Curator lifecycle + ForgetTool
- ✅ **Session Affinity Gateway** — Active-active multi-worker via Redis affinity + PubSub forwarding (smoke-verified 2026-05-14)
- ✅ **Web UI MVP** — Linear/Cursor visual refactor: Zustand state + virtualized message list + inline execution trace + Shiki code highlighting + multimodal (image paste/drop) + ⌘K command palette + global shortcuts + session CRUD (shipped 2026-05-15, see [report](./reports/optimize-web-ui-mvp-ship-2026-05-15.md))
- ✅ **MCP Client** — Anthropic Model Context Protocol integration: stdio servers, dual-name adapter pattern, per-server `forced_tier` (de-escalation only), `/mcp list/restart/logs`, non-blocking startup, `/health` advisory `mcp` field, secret redaction in logs (Sprint 2)
- ✅ **User Isolation + srt Sandbox** — Per-user tier profile + role-based permissions + `@anthropic-ai/sandbox-runtime` (macOS Seatbelt / Linux bubblewrap+seccomp), 11 architecture invariants (Sprint 3, shipped 2026-05-16)
- ✅ **Builtin Tier 2 Tools** — In-process `web_fetch` / `grep` / `glob` (sandbox-friendly, work under `tier=read-only`), optional `fetch_mcp_resource` (default-off), plus `.claude/skills/` cross-ecosystem 7-source discovery — cross-reads superpowers / Claude Code / OpenCode / Crush / Cursor 2.4 / DeepSeek-TUI ecosystems (Sprint 4', shipped 2026-05-18)
- ✅ **Skill Graduation** — High-frequency SOPs auto-graduate to SKILL.md (template / LLM-enrich modes, `pyclaw-skill curator graduate`, shipped 2026-05-07)
- ✅ **Sub-Agent System (A')** — Specialized agent personas (Markdown + YAML frontmatter), 5-layer discovery, depth-1 enforcement, parent-tier inheritance, `/agents` slash command, license-clean MIT distribution (Sprint 5, shipped 2026-05-18)
- 🔲 **Dreaming Engine** — Light/Deep/REM memory consolidation (extract → cluster → graph)
- 🔲 **PostgreSQL+pgvector** — production-grade memory backend (multi-pod K8s deployment)
- 🔲 **Plugin Platform (B)** — Unified extension protocol across 5 registries; **V2+ backlog**, triggered by ≥3 third-party plugin requests OR PyClaw user count ≥ 10k (per [strategic roadmap §四 anti-pattern #3](./DailyWork/planning/exploration/2026-05-15-strategic-roadmap-coding-agent.md): "marketplace infrastructure < 10k users is premature")

> **Canonical roadmap source**: [`DailyWork/planning/ROADMAP.md`](./DailyWork/planning/ROADMAP.md) (private). The list above is a high-level mirror only; for sprint status, dependency graph, and ship reports consult ROADMAP.md.

See [`openspec/`](./openspec/) for active changes and architectural specs.

---

## 🤝 Relationship to OpenClaw

PyClaw is inspired by [OpenClaw](https://github.com/openclaw/openclaw) and designed to be compatible with its skill ecosystem. PyClaw is an **independent Python reimplementation**, not a fork. It inherits the domain model (sessions, channels, skills) but redesigns the architecture with **memory as a first-class citizen**.

---

## 🧩 Pairs With: KnowMe (sibling project, pre-release)

PyClaw and **KnowMe / 知我** are sibling projects by the same author. Both follow the "pinyin + technical word" naming pattern. They solve **complementary halves of the agent-memory story**:

- **PyClaw** — the **agent runtime** (this repo): multi-channel (Feishu / Web), 4-layer L1-L4 memory, sub-agent system, sandbox, MCP client.
- **KnowMe** — the **MCP-native local-first knowledge vault**: SQLite + jieba FTS5, Markdown, Obsidian-compatible, exposes 5 read-only MCP tools.

> **Note**: KnowMe is currently a **private repository** (pre-release). Public launch is planned around v1 ship (~2026-07). When public, this section will link to the repo. Until then, treat KnowMe as architectural context for PyClaw's MCP-extension design.

**How they will compose** (once KnowMe is public + linked into PyClaw's `pyclaw.json`):

```
PyClaw agent  ──MCP query──▶  KnowMe vault          (today, read-only,
                                                     internal smoke verified)
            ◀──MCP write──   KnowMe                  (planned, KnowMe v1.5+)
            ─memorize─▶ PyClaw L4 in KnowMe vault    (planned)
```

PyClaw already supports KnowMe as an MCP server today — the wiring lives in [`configs/pyclaw.json`](./configs/pyclaw.json) under `mcp.servers.knowme`, but is **commented out / disabled by default** until KnowMe goes public. Internal smoke (2026-05-18): `list_pages` returns indexed wiki pages, `search "pyclaw"` returns jieba-tokenized full-text hits.

**Boundary contract** (because both projects ship independently):

- PyClaw's MCP client treats KnowMe as a **versioned external dependency** — any breaking change in KnowMe's tool schema must be announced before the breaking commit, with a documented changelog in the KnowMe repo (visible once KnowMe goes public).
- The reverse direction (PyClaw exposing itself **as** an MCP server, so KnowMe's future agent panel can call PyClaw) is a planned PyClaw OpenSpec change — tracked internally as `pyclaw-mcp-server-package`, not yet started.

**Why this exists at all**: the KB capability was originally Sprint 4 in PyClaw's roadmap; on 2026-05-18 it was reassigned to KnowMe so that PyClaw stays focused on the agent loop while KnowMe owns the vault. MCP is the protocol between them.

---

## 📡 Follow Us

**WeChat Official Account: Time留痕** — Deep dives on PyClaw development, AI Agent architecture, memory systems, and context engineering.

<div align="center">

<img src="./docs/assets/Time留痕.jpg" width="180" alt="WeChat: Time留痕" />

📚 **[完整文章合集 →](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzY5ODI5NzUwNA==&action=getalbum&album_id=4503553062812516353)**

</div>

---

## 🤝 Contributing

PRs welcome. The `openspec/` directory tracks all architectural changes — read the active proposals before opening big PRs. Small PRs (typo fixes, bug fixes) are always appreciated.

---

## 📜 License

[MIT License](./LICENSE) — free to use, modify, and distribute, including commercial use.
