Skill: garfeildma/openclaw-tavern (version 0.1.0)

--- name: OpenClaw RP Plugin description: SillyTavern-compatible roleplay plugin with character cards, long memory, multimodal output (TTS/image), and Generative-Agents-style companion. version: 0.1.0 homepage: https://github.com/garfeildma/openclaw-tavern user-invocable: true metadata: {"requires":{"env":["OPENCLAW_RP_LOCALE"],"bins":[],"install":[{"cmd":"npm install","when":"after-clone"}]}} --- # OpenClaw RP Plugin A full-featured roleplay (RP) extension for OpenClaw with first-class **SillyTavern** asset compatibility, multimodal abilities, long-term memory, and a Generative-Agents-style Companion system. ## What This Skill Does When installed, this plugin registers the `/rp` slash command namespace and three OpenClaw hooks (`message_received`, `before_prompt_build`, `llm_output`) to provide an immersive, persistent roleplay experience across Discord, Telegram, and OpenClaw native chat. ### Core Capabilities 1. **SillyTavern-Compatible Asset Import** — Import character cards (PNG tEXt/chara or JSON, V1/V2), presets, and lorebooks directly from SillyTavern format. 2. **Session Lifecycle** — Full state machine (`active → paused → summarizing → ended`) with per-session mutex, auto-summarization, and prompt budget trimming. 3. **Long Memory** — Turn-level embedding with SQLite persistence; retrieves relevant historical turns into `Relevant Memory Recall`. Built-in multilingual hashed embedder works out of the box without external APIs. 4. **Multimodal Output** — `/rp speak` (TTS), `/rp image` (image generation with style hints), and an optional `rp_generate_image` agent tool for native-agent use. 5. **Companion (Generative Agents Style)** — Memory Stream → Reflection → Planning loop for proactive outreach, follow-up questions, and action reporting via `/rp companion-nudge` and the `companion_tick` hook. ## Installation ### Via ClawHub CLI ```bash clawhub install openclaw-rp-plugin ``` ### Via OpenClaw Chat UI 1. Open your OpenClaw admin chat or plugin management session. 2. Use **Install Plugin / Install from Git** and paste: `https://github.com/garfeildma/openclaw-tavern` 3. Enable the plugin; verify ID `openclaw-rp-plugin`. 4. Send `/rp help` — if the command list appears, installation is complete. ### Post-Install Dependencies The plugin has two optional peer dependencies for enhanced functionality: - `better-sqlite3` (≥9.0) — enables SQLite persistence for sessions, assets, summaries, and memory vectors. Without it, data is stored in memory only. - `js-tiktoken` (≥1.0) — enables accurate cl100k token counting. Falls back to heuristic estimation without it. Install them in your OpenClaw workspace if needed: ```bash npm install better-sqlite3 js-tiktoken ``` ## Quick Start (3 Minutes) ### Step 1 — Import Assets ``` /rp import-card (attach a character card file) /rp import-preset (attach a preset file) /rp import-lorebook (attach a lorebook file, optional) ``` ### Step 2 — Start a Session ``` /rp start --card <card_name_or_id> --preset <preset_name_or_id> --lorebook <lorebook_name_or_id> ``` ### Step 3 — Chat Send plain messages to continue the story. The plugin intercepts dialogue automatically through the registered hooks. - Check status: `/rp session` - Pause / resume: `/rp pause` / `/rp resume` - Regenerate: `/rp retry [--edit "..."]` - End: `/rp end` ## Commands Reference | Command | Description | |---------|-------------| | `/rp help` | Show full command list | | `/rp import-card` | Import a SillyTavern character card (PNG or JSON) | | `/rp import-preset` | Import a SillyTavern preset | | `/rp import-lorebook` | Import a SillyTavern lorebook / world | | `/rp list-assets` | List imported assets (`--type`, `--search`, `--page`) | | `/rp show-asset <id>` | Show asset details | | `/rp delete-asset <id> --confirm` | Delete an asset | | `/rp start` | Start a new RP session | | `/rp session` | Show current session status | | `/rp retry [--edit "..."]` | Regenerate last reply, optionally with edited user turn | | `/rp speak` | TTS of last assistant reply | | `/rp image [--prompt] [--style]` | Image generation from RP context | | `/rp agent-image` | Manage agent image tool settings | | `/rp companion-nudge` | Trigger proactive companion outreach | | `/rp sync-agent-persona` | Write current RP character into Agent SOUL.md | | `/rp restore-agent-persona` | Remove RP character from SOUL.md, restore original | | `/rp pause` / `/rp resume` / `/rp end` | Session lifecycle control | ## Companion (Generative Agents) Trigger a proactive companion interaction: ``` /rp companion-nudge --force --reason "evening emotional check-in" --mode balanced /rp companion-nudge --idle-minutes 180 --mode checkin ``` Modes: `balanced`, `checkin`, `question`, `report`. The `companion_tick` hook can be wired to a scheduler for automatic proactive check-ins. ## Configuration ### Provider Resolution Priority 1. OpenClaw global `api.config` 2. `~/.openclaw/openclaw-rp/provider.json` 3. Environment variables (`OPENCLAW_RP_LOCALE`, `OPENAI_*`, `GEMINI_*`) ### Agent Image Tool Add to your OpenClaw config to expose `rp_generate_image` as an agent tool: ```json { "plugins": { "entries": { "openclaw-rp-plugin": { "config": { "agentImage": { "enabled": true, "provider": "openai", "imageModel": "gpt-image-1" } } } } } } ``` Then allow the tool in your agent config: ```json { "tools": { "profile": "messaging", "alsoAllow": ["rp_generate_image"] } } ``` ### Locale / i18n Supports Chinese (`zh`) and English (`en`). Resolution priority: `OPENCLAW_RP_LOCALE` → `provider.json` locale → `openclaw.json` locale → system `LANG` → default `zh`. ```bash export OPENCLAW_RP_LOCALE=en ``` ## Runtime Requirements - Node.js ≥ 20 - OpenClaw 2026.x - Optional: `better-sqlite3`, `js-tiktoken`, `ffmpeg` (for PCM→MP3 transcoding) ## Architecture Key entry points: - `src/openclaw/register.js` — Native OpenClaw extension registration (hooks & commands) - `src/plugin.js` — Plugin entry, hook wiring - `src/core/sessionManager.js` — Session lifecycle, summaries, long memory - `src/core/commandRouter.js` — `/rp` command routing - `src/core/promptBuilder.js` — Prompt assembly and budget management - `src/store/sqliteStore.js` — SQLite persistence layer

path: CHANGELOG.md
content: # Changelog

## 2026-03-19

### Added
- Added `SKILL.md` for ClawHub marketplace publishing, following the standard YAML frontmatter + markdown body format.
- Added ClawHub metadata to `openclaw.plugin.json` (author, homepage, license, tags, slug, category).
- Added npm registry metadata to `package.json` (description, author, license, repository, keywords).
- Added `clawhub:publish` convenience script for one-command publishing.

### Improved
- Added internationalization (i18n) support for all user-facing messages in the RP plugin. Supported locales: Chinese (`zh`) and English (`en`).
- Locale resolution priority: `OPENCLAW_RP_LOCALE` env var → `locale` field in `~/.openclaw/openclaw-rp/provider.json` → `locale` field in `~/.openclaw/openclaw.json` → system `LANG` env var → default `zh`.
- New module `src/openclaw/i18n.js` centralizes all translatable strings with `t(key)` accessor function.
- Added `/rp restore-agent-persona` command to remove the RP character preset block from `SOUL.md`, restoring the agent's original persona.

### Fixed
- Fixed session isolation when the plugin runs in global mode: RP context now keys on `conversationId` in addition to `channelId`, preventing cross-conversation message leakage where replies from one RP session leaked into another.
- Removed the unconditional fallback to the latest active session in `resolveActiveSessionForPending`, which was the root cause of cross-conversation pollution.

## 2026-03-13

### Improved
- Added the optional `rp_generate_image` agent tool so native OpenClaw agents can generate images in normal non-`/rp` chats and return them to the current IM conversation through a `MEDIA:` line.
- Added plugin config `agentImage.enabled / provider / imageModel` so operators can choose a dedicated provider and image model for agent-side generation without changing the `/rp` dialogue model.
- Image providers now support per-call `imageModel` overrides, making it possible to split image workloads across different models within the same provider stack.

## 2026-03-11

### Improved
- Added automatic Telegram media follow-ups for native RP sessions: when a user message implies "show me" or "let me hear your voice", the plugin can now auto-generate an image or voice reply after the normal assistant response.
- Added `/rp sync-agent-persona` to manually write the current RP character into the OpenClaw agent `SOUL.md` managed block.
- Added tests covering image and voice intent detection, Telegram auto media delivery, and manual agent persona synchronization.

## 2026-03-06

### Fixed
- Fixed an issue where assistant turns were not persisted correctly in the native OpenClaw RP dialogue flow.
- Fixed `/rp speak` and `/rp image` reading `first_message` or stale replies in some sessions instead of the latest role reply.
- Fixed RP context association during `llm_output` by tracking context with OpenClaw's stable `sessionKey` instead of incorrectly relying on `channelId` or a non-RP `sessionId`.
- Fixed premature RP context consumption when no valid assistant text was available, which could cause the real follow-up reply to be dropped.

### Improved
- Improved `/rp speak` TTS preprocessing so it synthesizes character dialogue by default instead of reading full narration or action text.
- Added dialogue extraction rules that prefer quoted speech from `“...”`, `"..."`, `「...」`, and `『...』`.
- Added a conservative fallback cleanup path for unquoted replies, stripping common action markers and short narration fragments such as `*...*`, `_..._`, and parenthetical asides while preserving speakable dialogue.
- Added tests covering RP hook context association and TTS text-cleaning behavior.


path: CHANGELOG.zh.md
content: # Changelog

## 2026-03-19

### Improved
- 新增国际化（i18n）支持，插件所有面向用户的提示语现在支持中文（`zh`）和英文（`en`）切换。
- 语言解析优先级：环境变量 `OPENCLAW_RP_LOCALE` → `~/.openclaw/openclaw-rp/provider.json` 中的 `locale` 字段 → `~/.openclaw/openclaw.json` 中的 `locale` 字段 → 系统 `LANG` 环境变量 → 默认 `zh`。
- 新增 `src/openclaw/i18n.js` 模块，统一管理所有可翻译字符串，通过 `t(key)` 函数访问。
- 新增 `/rp restore-agent-persona` 命令，可从 `SOUL.md` 中移除 RP 角色预设块，恢复 Agent 原始人设。

### Fixed
- 修复插件在全局模式下的会话隔离问题：RP 上下文现在同时基于 `conversationId` 和 `channelId` 进行键值隔离，防止不同会话之间的消息串扰（如一个 RP 会话的回复错误出现在另一个会话中）。
- 移除 `resolveActiveSessionForPending` 中无条件回退到同 channel type 最新活跃会话的逻辑，该逻辑是跨会话消息泄漏的根本原因。

## 2026-03-13

### Improved
- 新增可选 agent tool `rp_generate_image`，让 OpenClaw 原生 agent 在非 `/rp` 普通对话中也能调用插件的生图 provider，并通过 `MEDIA:` 回传图片到当前 IM 会话。
- 新增插件配置 `agentImage.enabled / provider / imageModel`，可单独指定 agent 生图所使用的 provider 与模型，不影响 `/rp` 对话主模型。
- image provider 现在支持按调用覆盖 `imageModel`，便于在同一 provider 栈下为不同能力分配不同生图模型。

## 2026-03-11

### Improved
- 为 OpenClaw 原生 RP 的 Telegram 会话新增自动媒体补发能力：当用户消息表达出“想看照片”或“想听语音”的意思时，插件会在正常文字回复完成后自动补发图片或语音。
- 新增 `/rp sync-agent-persona` 命令，可手动将当前 RP 角色写入 OpenClaw agent 的 `SOUL.md` 受管区块。
- 补充测试，覆盖图片/语音意图识别、Telegram 自动媒体投递，以及手动同步 Agent 人设等场景。

## 2026-03-06

### Fixed
- 修复 OpenClaw 原生 RP 对话链路中 assistant turn 未正确落库的问题。
- 修复 `/rp speak` 和 `/rp image` 在部分会话中错误读取 `first_message` 或旧回复的问题。
- 修复 `llm_output` 阶段对 RP 上下文的关联方式，改为基于 OpenClaw 稳定传递的 `sessionKey` 追踪上下文，而不再错误依赖 `channelId` / 非 RP `sessionId`。
- 修复 `llm_output` 在未拿到有效 assistant 文本时过早消费上下文，导致后续真实回复丢失的问题。

### Improved
- 优化 `/rp speak` 的 TTS 文本提取逻辑，默认只合成角色对白，不再直接朗读整段旁白/动作描述。
- 新增对白抽取规则：优先提取 `“...”`、`"..."`、`「...」`、`『...』` 中的台词。
- 在无引号对白时，对 TTS 输入做保守降噪，去除常见动作标记与简短旁白（如 `*...*`、`_..._`、括号说明），保留可朗读台词。
- 补充相关测试，覆盖 RP hook 上下文关联与 TTS 文本清洗场景。


path: README.md
content: # OpenClaw RP Plugin (SillyTavern Compatible)

[中文 README](./README.zh.md) | [中文 Architecture](./docs/ARCHITECTURE.zh-CN.md) | [English Architecture](./docs/ARCHITECTURE.md)

OpenClaw RP Plugin is a roleplay-focused OpenClaw plugin with first-class SillyTavern asset compatibility, multimodal output, and long-memory support.

> Featured update: a Companion design inspired by [Generative Agents: Interactive Simulacra of Human Behavior](https://arxiv.org/abs/2304.03442), enabling long-memory-driven proactive outreach, proactive questioning, and action reporting.

## Who This Is For

- Users migrating cards/presets/lorebooks from SillyTavern to OpenClaw
- Users running RP across Discord, Telegram, and OpenClaw native message flow
- Users who prefer command-driven session and asset management

## Feature Highlights

### 1. SillyTavern-Compatible Imports

- Character cards: `PNG (tEXt/chara)` and `JSON`
- Card versions: V1 and V2 supported, with unmapped fields preserved
- Preset import: SillyTavern JSON mapping
- Lorebook import: ST world/lorebook JSON mapping
- Import inputs:
  - Direct attachment
  - `--url`
  - `--file`
- In OpenClaw native mode, users can send file first, then run `/rp import-*`

### 2. Session Lifecycle and Context Control

- Session states: `active / paused / summarizing / ended`
- Per-session mutex to prevent race conditions
- `retry` / `retry --edit` to regenerate last assistant turn
- Auto-summarization when context exceeds threshold
- Prompt budgeting with deterministic truncation priority

### 3. Long Memory

- Turn-level embedding storage (persisted in SQLite when enabled)
- Retrieval of relevant historical turns into `Relevant Memory Recall`
- Built-in multilingual hashed embedding (works without external embedding API)
- Pluggable external embedding providers (OpenAI / Gemini, etc.)

### 4. Multimodal

- `/rp speak`: TTS from latest assistant reply
- `/rp image`: image generation from role context, supports `--prompt` / `--style`
- `/rp agent-image`: inspect or switch native-agent image provider / model / enabled state
- Optional agent tool: `rp_generate_image`, which lets the native OpenClaw agent generate and return images in normal non-`/rp` chats
- Built-in multimodal rate limit (default 5s window)

### 5. Native OpenClaw Integration

- Command registration: `/rp`
- Hook integration: `message_received`, `before_prompt_build`, `llm_output`
- Inherits OpenClaw global model config when available
- Supports OpenAI-compatible and Gemini provider stacks
- SQLite persistence for assets, sessions, summaries, and memory vectors
- Session isolation in global mode: RP contexts are keyed by both `conversationId` and `channelId` to prevent cross-conversation leakage

### ⭐ Focus: Companion Agent (Generative Agents Style) [WIP]

- Uses a lightweight `Memory Stream -> Reflection -> Planning` behavior loop
- Reuses `rp_turn_embeddings` retrieval to personalize proactive outreach from long-term memory
- Adds `/rp companion-nudge` for:
  - proactive messages to the user
  - proactive follow-up questions
  - explicit action reports on what the character tracked and will follow up on
- Adds `companion_tick` hook for scheduler/automation-driven proactive check-ins
- Keeps existing dialogue flow unchanged by default (incremental feature); can be disabled via `contextPolicy.companionEnabled = false`

## Beginner Install (OpenClaw Chat UI)

Note: install entry names vary by gateway version (plugin manager button vs admin command). Use this version-safe flow:

1. Open your OpenClaw admin chat (or plugin management chat).
2. Use "Install Plugin / Install from Git" and paste this repo URL.
   - If your gateway uses command-style install, use the command shown by your gateway (a common pattern is `/plugins install <repo-url>`).
3. Enable the plugin and verify ID `openclaw-rp-plugin`.
4. Send `/rp help` in chat. If command list appears, installation is complete.

## 3-Min Quick Start

### Step 1: Import Assets

```text
/rp import-card      (attach a card file)
/rp import-preset    (attach a preset file)
/rp import-lorebook  (attach a lorebook file, optional)
```

### Step 2: Start Session

```text
/rp start --card <card_name_or_id> --preset <preset_name_or_id> --lorebook <lorebook_name_or_id>
```

### Step 3: Chat Normally

- Send plain messages to continue the story
- Check status: `/rp session`
- Pause/resume: `/rp pause` / `/rp resume`
- End: `/rp end`

## Common Commands

- `/rp help`
- `/rp import-card` / `/rp import-preset` / `/rp import-lorebook`
- `/rp list-assets [--type card|preset|lorebook] [--search "..."] [--page N]`
- `/rp show-asset <name_or_id>`
- `/rp delete-asset <id> --confirm`
- `/rp start --card ... [--preset ...] [--lorebook ...]`
- `/rp session`
- `/rp retry [--edit "..."]`
- `/rp speak`
- `/rp image [--prompt "..."] [--style "..."]`
- `/rp agent-image [--provider inherit|openai|gemini] [--model "..."] [--clear-model] [--enable|--disable]`
- `/rp companion-nudge [--reason "..."] [--idle-minutes N] [--mode balanced|checkin|question|report] [--force]`
- `/rp sync-agent-persona` — write current RP character into the agent's `SOUL.md`
- `/rp restore-agent-persona` — remove RP character preset from `SOUL.md`, restore original persona
- `/rp pause` / `/rp resume` / `/rp end`

## Companion Quick Examples

```text
# Trigger proactive companion output now (message + question + action report)
/rp companion-nudge --force --reason "evening emotional check-in" --mode balanced

# Trigger only when user has been idle for 3 hours
/rp companion-nudge --idle-minutes 180 --mode checkin
```

`companion_tick` hook input example (for scheduler/automation):

```json
{
  "session_id": "session_xxx",
  "user_id": "u1",
  "reason": "daily check-in",
  "mode": "balanced",
  "idle_minutes": 120
}
```

## Configuration (for operators)

### Runtime Requirements

- Node.js `>=20`
- Optional dependencies:
  - `better-sqlite3` (SQLite persistence)
  - `js-tiktoken` (cl100k token estimator)

### Provider Resolution Priority

1. OpenClaw global `api.config`
2. `~/.openclaw/openclaw-rp/provider.json`
3. Environment variables (`OPENCLAW_RP_*`, `OPENAI_*`, `GEMINI_*`)

### Agent Image Tool Config

Add plugin config under your OpenClaw config:

```json
{
  "plugins": {
    "entries": {
      "openclaw-rp-plugin": {
        "config": {
          "agentImage": {
            "enabled": true,
            "provider": "openai",
            "imageModel": "gpt-image-1"
          }
        }
      }
    }
  }
}
```

- `agentImage.enabled`: exposes the `rp_generate_image` tool
- `agentImage.provider`: `inherit`, `openai`, or `gemini`
- `agentImage.imageModel`: overrides only the agent image-generation model, without changing the `/rp` dialogue model

To let an OpenClaw agent use it, also allow `rp_generate_image` in the agent tool config. On OpenClaw `2026.3.x`, the recommended config is:

```json
{
  "tools": {
    "profile": "messaging",
    "alsoAllow": ["rp_generate_image"]
  }
}
```

The tool returns a `MEDIA:...` line, and the agent should keep that line in its final reply so the image is sent back to the current IM conversation.

Notes:

- If you use an OpenAI-compatible image endpoint such as Grok, set `agentImage.provider` to `openai`
- If you use Google Gemini image generation, set `agentImage.provider` to `gemini`
- For OpenAI-compatible gateways, `agentImage.imageModel` must exactly match the model `id` returned by `/v1/models`; for example, this gateway exposes `grok-imagine-1.0`, not `grok/grok-imagine-1.0`
- After changing tool config or plugin schema, existing sessions may still have a stale tool list; send `/new` before testing again

You can also switch it directly in native OpenClaw mode:

```bash
/rp agent-image
/rp agent-image --provider openai --model grok-imagine-1.0
/rp agent-image --provider gemini --model gemini-3.1-flash-image-preview
/rp agent-image --clear-model
/rp agent-image --disable
/rp agent-image --enable
```

This command updates `plugins.entries.openclaw-rp-plugin.config.agentImage` and refreshes the live in-process agent image config immediately, without restarting the gateway.

### Locale / i18n

The plugin supports Chinese (`zh`) and English (`en`) for all user-facing messages (session status, persona sync, help text, etc.).

Locale resolution priority:

1. Environment variable `OPENCLAW_RP_LOCALE` (e.g. `en` or `zh`)
2. `locale` field in `~/.openclaw/openclaw-rp/provider.json`
3. `locale` field in `~/.openclaw/openclaw.json`
4. System `LANG` environment variable (e.g. `en_US.UTF-8` → `en`)
5. Default: `zh`

Example — switch to English:

```bash
export OPENCLAW_RP_LOCALE=en
```

Or add to `~/.openclaw/openclaw.json`:

```json
{
  "locale": "en"
}
```

## Roadmap

- As a long-term emotional companion, learn to proactively care <Generative Agents: Interactive Simulacra of Human Behavior>

## Development & Testing

```bash
npm test
npm run smoke
```

Key entry points:

- `src/openclaw/register.js` (native OpenClaw registration)
- `src/plugin.js` (plugin entry and hooks)
- `src/core/sessionManager.js` (session, summary, long memory)
- `src/core/commandRouter.js` (`/rp` command router)
- `src/core/promptBuilder.js` (prompt assembly and budget)
- `src/store/sqliteStore.js` (SQLite persistence)


path: README.zh.md
content: # OpenClaw RP Plugin（SillyTavern 兼容）

[English README](./README.md) | [中文架构文档](./docs/ARCHITECTURE.zh-CN.md) | [English Architecture](./docs/ARCHITECTURE.md)

OpenClaw RP Plugin 是一个面向角色扮演（RolePlay）的 OpenClaw 插件，重点兼容 SillyTavern 资产生态，并提供多模态能力与长记忆能力。

> 重点更新：基于 [Generative Agents: Interactive Simulacra of Human Behavior](https://arxiv.org/abs/2304.03442) 的 Companion 设计，支持长期记忆驱动的主动关怀（主动消息、主动询问、行动汇报）。

## 适用人群

- 希望把 SillyTavern 角色卡/Preset/Lorebook 迁移到 OpenClaw 的用户
- 想在 Discord / Telegram / OpenClaw 原生消息链路中统一使用 RP 的用户
- 希望以命令方式快速管理会话与资产的用户

## 特性总览

### 1. SillyTavern 兼容导入

- 角色卡：支持 `PNG (tEXt/chara)` 与 `JSON`
- 角色卡版本：兼容 V1 / V2，并保留未映射字段
- Preset：兼容 ST JSON 预设字段映射
- Lorebook：兼容 ST World/Lorebook JSON
- 资产导入支持三种输入：
  - 直接附件
  - `--url`
  - `--file`
- OpenClaw 原生插件模式支持“先发文件，再执行 `/rp import-*`”

### 2. 会话管理与上下文控制

- 会话状态机：`active / paused / summarizing / ended`
- 每会话互斥锁（per-session mutex），避免并发串话
- `retry` / `retry --edit` 回退并重生最后一轮回复
- 自动摘要（超过阈值触发），降低上下文膨胀
- Prompt 预算裁剪：固定保留角色关键信息，优先保留近期对话

### 3. 长记忆（Long Memory）

- 对话轮次写入 embedding（可持久化到 SQLite）
- 查询时召回历史“相关记忆”，注入 Prompt 的 `Relevant Memory Recall`
- 默认内置多语言哈希 embedding（无需外部 embedding 服务也可运行）
- 可切换为外部 embedding provider（OpenAI / Gemini 等）

### 4. 多模态能力

- `/rp speak`：对最近一条角色回复进行语音合成（TTS）
- `/rp image`：基于角色上下文生成图像，可加 `--prompt` / `--style`
- `/rp agent-image`：查看或切换原生 agent 生图 provider / model / enabled
- 可选 agent tool：`rp_generate_image`，让 OpenClaw 自身 agent 在非 `/rp` 普通对话中也能生图并回传到 IM
- 多模态请求自带限流（默认 5 秒窗口）

### 5. OpenClaw 原生集成

- 注册命令：`/rp`
- Hook 集成：`message_received`、`before_prompt_build`、`llm_output`
- 自动继承 OpenClaw 全局模型配置，支持 OpenAI-compatible / Gemini provider
- SQLite 默认持久化会话、资产、摘要与记忆向量
- 全局模式下的会话隔离：RP 上下文基于 `conversationId` + `channelId` 联合键值隔离，防止跨会话消息串扰

### Companion 情感伙伴（Generative Agents 风格）[WIP]

- 采用“记忆流（Memory Stream）→ 反思（Reflection）→ 计划（Planning）”的角色行为设计
- 长期记忆直接复用 `rp_turn_embeddings` 召回结果，主动关怀会参考历史偏好与关键事件
- 提供 `/rp companion-nudge`：
  - 主动给用户发消息
  - 主动发起追问
  - 主动汇报角色当前行动（已记录了什么、下一步怎么跟进）
- 提供 `companion_tick` hook（可接你的定时器/自动化任务），在空闲阈值满足时自动触发主动关怀
- 默认不改变现有对话主链路，属于增量能力；关闭方式：`contextPolicy.companionEnabled = false`

## 快速安装（通过 OpenClaw 聊天界面）

说明：不同 OpenClaw 网关版本的安装入口名称可能不同（有的为插件管理按钮，有的为管理员命令）。下面给出最稳妥流程。

1. 打开 OpenClaw 管理员聊天窗口（或插件管理会话）。
2. 选择“安装插件 / Install from Git”入口，粘贴本仓库地址并安装。
   - 若你的网关是命令式安装，使用网关提示的安装命令（常见形态是 `/plugins install <repo-url>`）。
3. 安装后启用插件，确认插件 ID 为 `openclaw-rp-plugin`。
4. 在任意会话发送 `/rp help`，看到命令列表即代表安装成功。

## 3 分钟上手

### Step 1: 导入资产

```text
/rp import-card      （附角色卡文件）
/rp import-preset    （附 preset 文件）
/rp import-lorebook  （附 lorebook 文件，可选）
```

### Step 2: 启动会话

```text
/rp start --card <角色名或ID> --preset <预设名或ID> --lorebook <知识书名或ID>
```

### Step 3: 直接聊天

- 发送普通消息即可继续剧情
- 查看状态：`/rp session`
- 暂停/恢复：`/rp pause` / `/rp resume`
- 结束：`/rp end`

## 常用命令

- `/rp help`
- `/rp import-card` / `/rp import-preset` / `/rp import-lorebook`
- `/rp list-assets [--type card|preset|lorebook] [--search "..."] [--page N]`
- `/rp show-asset <name_or_id>`
- `/rp delete-asset <id> --confirm`
- `/rp start --card ... [--preset ...] [--lorebook ...]`
- `/rp session`
- `/rp retry [--edit "..."]`
- `/rp speak`
- `/rp image [--prompt "..."] [--style "..."]`
- `/rp agent-image [--provider inherit|openai|gemini] [--model "..."] [--clear-model] [--enable|--disable]`
- `/rp companion-nudge [--reason "..."] [--idle-minutes N] [--mode balanced|checkin|question|report] [--force]`
- `/rp sync-agent-persona` — 将当前 RP 角色写入 Agent 的 `SOUL.md`
- `/rp restore-agent-persona` — 从 `SOUL.md` 移除 RP 角色预设，恢复原始人设
- `/rp pause` / `/rp resume` / `/rp end`

## Companion 快速示例

```text
# 立即触发一次主动关怀（主动消息 + 追问 + 行动汇报）
/rp companion-nudge --force --reason "晚间关心一下用户今天状态" --mode balanced

# 只在用户空闲超过 3 小时后触发
/rp companion-nudge --idle-minutes 180 --mode checkin
```

`companion_tick` hook 输入示例（用于定时任务/自动化触发）：

```json
{
  "session_id": "session_xxx",
  "user_id": "u1",
  "reason": "daily check-in",
  "mode": "balanced",
  "idle_minutes": 120
}
```

## 配置说明（部署者）

### 运行环境

- Node.js `>=20`
- 可选依赖：
  - `better-sqlite3`（SQLite 持久化）
  - `js-tiktoken`（cl100k token estimator）

### Provider 来源优先级

1. OpenClaw 全局 `api.config`
2. `~/.openclaw/openclaw-rp/provider.json`
3. 环境变量（如 `OPENCLAW_RP_*`, `OPENAI_*`, `GEMINI_*`）

### Agent 生图工具配置

在 OpenClaw 配置里为插件增加：

```json
{
  "plugins": {
    "entries": {
      "openclaw-rp-plugin": {
        "config": {
          "agentImage": {
            "enabled": true,
            "provider": "openai",
            "imageModel": "gpt-image-1"
          }
        }
      }
    }
  }
}
```

- `agentImage.enabled`：是否暴露 `rp_generate_image` tool
- `agentImage.provider`：`inherit` / `openai` / `gemini`
- `agentImage.imageModel`：单独覆盖 agent 生图模型，不影响 `/rp` 主对话模型

如果要让某个 OpenClaw agent 使用它，还需要把 `rp_generate_image` 加进 agent 的额外工具名单。在 OpenClaw `2026.3.x` 上，推荐配置为：

```json
{
  "tools": {
    "profile": "messaging",
    "alsoAllow": ["rp_generate_image"]
  }
}
```

该 tool 会返回 `MEDIA:...` 行，agent 在最终回复中保留这行即可把图片发回当前 IM 会话。

补充说明：

- 如果你使用 Grok 之类的 OpenAI-compatible 生图接口，`agentImage.provider` 应设为 `openai`
- 如果你使用 Google Gemini 生图接口，`agentImage.provider` 应设为 `gemini`
- 对 OpenAI-compatible 网关，`agentImage.imageModel` 必须与 `/v1/models` 返回的模型 `id` 完全一致；例如这次网关实际提供的是 `grok-imagine-1.0`，不是 `grok/grok-imagine-1.0`
- 修改 tool 配置或插件 schema 后，已有旧 session 可能拿不到最新工具列表；先发一次 `/new` 再测试

也可以直接用命令在 OpenClaw 原生模式下切换：

```bash
/rp agent-image
/rp agent-image --provider openai --model grok-imagine-1.0
/rp agent-image --provider gemini --model gemini-3.1-flash-image-preview
/rp agent-image --clear-model
/rp agent-image --disable
/rp agent-image --enable
```

这个命令会更新 `plugins.entries.openclaw-rp-plugin.config.agentImage`，并立即刷新当前网关进程内的 agent 生图配置，不需要重启。

### 语言 / 国际化（i18n）

插件所有面向用户的提示语支持中文（`zh`）和英文（`en`）切换，包括会话状态提示、人设同步消息、帮助文本等。

语言解析优先级：

1. 环境变量 `OPENCLAW_RP_LOCALE`（如 `en` 或 `zh`）
2. `~/.openclaw/openclaw-rp/provider.json` 中的 `locale` 字段
3. `~/.openclaw/openclaw.json` 中的 `locale` 字段
4. 系统 `LANG` 环境变量（如 `zh_CN.UTF-8` → `zh`）
5. 默认：`zh`

示例 — 切换为英文：

```bash
export OPENCLAW_RP_LOCALE=en
```

或在 `~/.openclaw/openclaw.json` 中添加：

```json
{
  "locale": "en"
}
```

## Roadmap

- [] 作为长期情感伴侣，学会主动关心 <Generative Agents: Interactive Simulacra of Human Behavior>

## 开发与测试

```bash
npm test
npm run smoke
```

主要代码入口：

- `src/openclaw/register.js`（OpenClaw 原生注册）
- `src/plugin.js`（插件入口与 hooks）
- `src/core/sessionManager.js`（会话、摘要、长记忆）
- `src/core/commandRouter.js`（`/rp` 命令路由）
- `src/core/promptBuilder.js`（Prompt 组装与预算）
- `src/store/sqliteStore.js`（SQLite 持久化）


path: docs/ARCHITECTURE.md
content: # OpenClaw RP Plugin Architecture

[中文版本](./ARCHITECTURE.zh-CN.md)

This document explains the implementation architecture, module boundaries, and extension points of the OpenClaw RP plugin for contributors.

## 1. Design Goals

- SillyTavern asset compatibility (Card/Preset/Lorebook)
- Minimal-intrusion integration into OpenClaw plugin runtime
- Stable session control (state machine + mutex + retry)
- Multimodal output and long-memory support (summary + embedding recall)

## 2. Top-Level Modules

- `src/openclaw/register.js`
  - Native OpenClaw registration entry
  - Registers `/rp` command and hooks: `message:preprocessed`, `message_received`, `before_prompt_build`, `llm_output`
  - Resolves provider config and initializes SQLite store
- `src/plugin.js`
  - Generic plugin factory: `createRPPlugin()`
  - Composes `CommandRouter`, `SessionManager`, and Store
- `src/core/commandRouter.js`
  - Parses/routes `/rp` commands
  - Handles import, session lifecycle, retry, multimodal commands
- `src/core/sessionManager.js`
  - Main dialogue pipeline
  - Session mutex, summary trigger, prompt preparation, memory indexing and recall
- `src/core/promptBuilder.js`
  - Deterministic prompt assembly order
  - Token budget allocation and truncation
- `src/store/sqliteStore.js` / `src/store/inMemoryStore.js`
  - Persistence and test store implementations
- `src/importers/*.js`
  - ST asset parsing/mapping
- `src/providers/*.js`
  - OpenAI-compatible and Gemini provider adapters

## 3. Runtime Flow

```mermaid
flowchart LR
  A["User Message"] --> B["OpenClaw /rp Command Or Hook"]
  B --> C["CommandRouter"]
  C --> D["SessionManager"]
  D --> E["PromptBuilder"]
  E --> F["modelProvider.generate"]
  F --> D
  D --> G["Store (SQLite/InMemory)"]
  C --> H["ttsProvider/imageProvider"]
```

### 3.1 Command Path (`/rp`)

1. `parseRpCommand()` parses command and options.
2. `CommandRouter` dispatches subcommands:
   - `import-*`: parse/map/store assets
   - `start/session/pause/resume/end`: session lifecycle
   - `retry`: rollback latest assistant turn and regenerate
   - `speak/image`: multimodal branches
3. Returns normalized response envelope: `{ ok, code, message, data }`.

### 3.2 Dialogue Path (non-command message)

1. `message_received` enters `SessionManager.processDialogue()`.
2. Session is resolved via `channel_session_key`.
3. Per-session mutex serializes processing.
4. User turn is appended; summarization may trigger.
5. Prompt is prepared and model config resolved.
6. `modelProvider.generate()` is invoked.
7. Assistant turn is appended and embedding index is updated.

### 3.3 Native OpenClaw Hook Injection Path

`register.js` additionally supports native OpenClaw message flow:

- `message_received`: append user turn to RP session and keep routing context
- `before_prompt_build`: inject RP prompt into OpenClaw main model request
- `llm_output`: persist model output as assistant turn

This enables RP behavior to cooperate with OpenClaw’s core LLM pipeline, not only `/rp` command responses.

## 4. Prompt Assembly Strategy

`buildPrompt()` default order:

1. `system_prompt`
2. Character core block (name/description/personality/scenario)
3. Matched lorebook entries
4. Example dialogue
5. Summary
6. Memory recall (`Relevant Memory Recall`)
7. Recent turns
8. `post_history_instructions`

Default budgets (configurable):

- `maxPromptTokens = 8000`
- `lorebook = 2000`
- `example = 1000`
- `summary = 1000`
- `memory = 900`

## 5. Long Memory Implementation

### 5.1 Indexing

- User/assistant turns can be persisted in `rp_turn_embeddings`
- Language tag is heuristically detected (`detectLanguageTag()`)
- Embedding source:
  - external provider (OpenAI/Gemini)
  - built-in fallback (`hashed-multilingual-*`)

### 5.2 Retrieval

- Entry point: `searchTurnEmbeddings()`
- Uses SQLite vector distance function when extension is available
- Falls back to JS cosine similarity otherwise
- Results are deduplicated and injected into prompt memory section

## 6. Session and Consistency Controls

- Session key format: `{channel_type}:{platform_context_id}:{channel_id}:{user_id}`
- Session states: `active / paused / summarizing / ended`
- Concurrency: `SessionMutex`
- Error model: `RPError` + unified `RP_*` error codes
- Retry policy:
  - dialogue generation with backoff retry
  - independent summary retry config
  - independent multimodal timeout/retry

## 7. Data Model (SQLite)

Core tables (`src/store/schema.js`):

- Assets: `rp_assets`, `rp_cards`, `rp_presets`, `rp_lorebooks`
- Sessions: `rp_sessions`, `rp_session_lorebooks`
- Dialogue: `rp_turns`, `rp_summaries`
- Memory vectors: `rp_turn_embeddings`

Key constraints:

- Partial unique index on active session statuses in `rp_sessions`
- FK cascade to preserve referential consistency

## 8. Provider Abstraction

`createRPPlugin()` uses DI-style provider contracts:

- `modelProvider.generate/summarize`
- `ttsProvider.synthesize`
- `imageProvider.generate`
- `embeddingProvider.embed`

In native OpenClaw mode, `register.js` resolves providers from `api.config`, `provider.json`, and env vars, then selects OpenAI-compatible or Gemini stacks.

## 9. Extension Points and Contribution Guidance

### Practical Extension Points

- New model backend: add provider adapter and inject
- New import format: add parser under `src/importers/`
- New context policy: tune/extend `contextPolicy`
- New channel integration: add adapter normalization/send mapping

### Contribution Guidance

- Add/adjust tests before behavior changes (`tests/` covers core paths)
- Keep response envelope backward compatible (`ok/code/message/data`)
- Avoid mixing storage details into router layer; keep boundaries explicit

## 10. Known Boundaries

- Vector retrieval quality depends on runtime availability of SQLite vector extension
- Native install entry naming may differ by OpenClaw version (command-based vs UI-based)
- Current UX is still command-first (no dedicated Web management console yet)


path: examples/openclaw-rp-bootstrap.js
content: import Database from "better-sqlite3";
import {
  createRPPlugin,
  createOpenAICompatibleProviders,
  createHttpAttachmentResolver,
  createTelegramAttachmentResolver,
  composeAttachmentResolvers,
  createDiscordMessageHandler,
  createTelegramUpdateHandler,
} from "../src/index.js";

function createPlugin() {
  const db = new Database("./rp.sqlite");

  const providers = createOpenAICompatibleProviders({
    baseUrl: process.env.OPENAI_BASE_URL || "https://api.openai.com/v1",
    [REDACTED_SECRET],
    model: process.env.OPENAI_MODEL || "gpt-4o-mini",
    ttsModel: process.env.OPENAI_TTS_MODEL || "gpt-4o-mini-tts",
    imageModel: process.env.OPENAI_IMAGE_MODEL || "gpt-image-1",
  });

  const resolvers = [createHttpAttachmentResolver()];
  if (process.env.TELEGRAM_BOT_TOKEN) {
    resolvers.push(
      createTelegramAttachmentResolver({
        botToken: process.env.TELEGRAM_BOT_TOKEN,
      }),
    );
  }
  const attachmentResolver = composeAttachmentResolvers(...resolvers);

  return createRPPlugin({
    sqliteDb: db,
    ...providers,
    attachmentResolver,
    hookTimeoutMs: 10000,
  });
}

export function createHandlers({ discordSend, telegramSend }) {
  const plugin = createPlugin();

  const onDiscordEvent = createDiscordMessageHandler({
    plugin,
    send: discordSend,
  });

  const onTelegramUpdate = createTelegramUpdateHandler({
    plugin,
    send: telegramSend,
  });

  return {
    plugin,
    onDiscordEvent,
    onTelegramUpdate,
  };
}


path: examples/smoke-run.js
content: import { createRPPlugin } from "../src/index.js";

function ctx(content, extra = {}) {
  return {
    channelType: "discord",
    platformContextId: "guild-dev",
    channelId: "chan-dev",
    userId: "user-dev",
    content,
    attachments: [],
    ...extra,
  };
}

const plugin = createRPPlugin({
  modelProvider: {
    async generate() {
      return { content: "[mock assistant reply]" };
    },
    async summarize({ conversation }) {
      return `[summary] ${conversation.slice(0, 50)}`;
    },
  },
  ttsProvider: {
    async synthesize() {
      return { audioUrl: "https://example.local/audio.mp3" };
    },
  },
  imageProvider: {
    async generate() {
      return { imageUrl: "https://example.local/image.png" };
    },
  },
});

async function run() {
  const card = await plugin.hooks.message_received(
    ctx("/rp import-card", {
      attachments: [{ filename: "card.json", buffer: Buffer.from(JSON.stringify({ name: "SmokeCard", first_mes: "Hello" })) }],
    }),
  );
  const cardId = card.response.data.asset_id;

  const preset = await plugin.hooks.message_received(
    ctx("/rp import-preset", {
      attachments: [{ filename: "preset.json", buffer: Buffer.from(JSON.stringify({ temperature: 0.7 })) }],
    }),
  );
  const presetId = preset.response.data.asset_id;

  console.log((await plugin.hooks.message_received(ctx(`/rp start --card ${cardId} --preset ${presetId}`))).response);
  console.log((await plugin.hooks.message_received(ctx("hello"))).response);
  console.log((await plugin.hooks.message_received(ctx("/rp speak"))).response);
  console.log((await plugin.hooks.message_received(ctx('/rp image --prompt "smoke test"'))).response);
  console.log((await plugin.hooks.message_received(ctx("/rp end"))).response);
}

run().catch((err) => {
  console.error(err);
  process.exitCode = 1;
});


path: src/utils/id.js
content: import crypto from "node:crypto";

export function makeId(prefix) {
  const raw = crypto.randomBytes(6).toString("base64url");
  return `${prefix}_${raw}`;
}

export function sha256(value) {
  return crypto.createHash("sha256").update(value).digest("hex");
}


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)