diff --git a/.github/workflows/_build-docs.yml b/.github/workflows/_build-docs.yml
index 8bc9691d..338c0ec6 100644
--- a/.github/workflows/_build-docs.yml
+++ b/.github/workflows/_build-docs.yml
@@ -28,6 +28,7 @@ jobs:
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
+ fetch-depth: 0
persist-credentials: false
- name: Set up Node
diff --git a/.github/workflows/ci-docs.yml b/.github/workflows/ci-docs.yml
index d506b005..490920db 100644
--- a/.github/workflows/ci-docs.yml
+++ b/.github/workflows/ci-docs.yml
@@ -11,11 +11,17 @@ on:
- 'README_ZH.md'
- 'docs/**'
- 'github-pages/**'
+ - 'reme/config/default.yaml'
+ - 'integrations/claude_code/README.md'
+ - 'integrations/hermes_agent/README.md'
- 'reme_studio/README*.md'
- 'reme_studio/public/og.jpg'
- 'typescript/README*.md'
+ - 'typescript/docs/**'
+ - 'typescript/figures/**'
- 'plugins/*/README*.md'
- 'benchmark/*/README*.md'
+ - 'benchmark/toolmemory/gitcha.png'
pull_request:
branches: [main, master, dev, develop]
paths:
@@ -26,11 +32,17 @@ on:
- 'README_ZH.md'
- 'docs/**'
- 'github-pages/**'
+ - 'reme/config/default.yaml'
+ - 'integrations/claude_code/README.md'
+ - 'integrations/hermes_agent/README.md'
- 'reme_studio/README*.md'
- 'reme_studio/public/og.jpg'
- 'typescript/README*.md'
+ - 'typescript/docs/**'
+ - 'typescript/figures/**'
- 'plugins/*/README*.md'
- 'benchmark/*/README*.md'
+ - 'benchmark/toolmemory/gitcha.png'
workflow_dispatch:
concurrency:
diff --git a/.github/workflows/ci-python-quality.yml b/.github/workflows/ci-python-quality.yml
index 21cd7a87..d39302f6 100644
--- a/.github/workflows/ci-python-quality.yml
+++ b/.github/workflows/ci-python-quality.yml
@@ -34,7 +34,7 @@ jobs:
- name: Install
run: |
pip install -q -e reme_studio -e ".[dev,core]"
- pip install -q --no-deps -e plugins/auto-fin -e plugins/daily_paper
+ pip install -q --no-deps -e plugins/auto-fin -e plugins/daily_paper -e plugins/lme -e plugins/beam
- name: Pre-commit starts
run: pre-commit run --all-files
diff --git a/.github/workflows/ci-python-tests.yml b/.github/workflows/ci-python-tests.yml
index 00b3bbbf..9aab1e75 100644
--- a/.github/workflows/ci-python-tests.yml
+++ b/.github/workflows/ci-python-tests.yml
@@ -40,11 +40,12 @@ jobs:
pip install -e reme_studio -e ".[dev,core]"
pip install --no-deps -e plugins/auto-fin
pip install -e plugins/daily_paper
+ pip install -e plugins/lme -e plugins/beam
pip install coverage
- name: Run unit tests
run: |
- coverage run -m pytest tests/unit plugins/auto-fin plugins/daily_paper \
+ coverage run -m pytest tests/unit plugins/auto-fin plugins/daily_paper plugins/lme plugins/beam \
-v \
--tb=long \
-s \
diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
index fe81de3d..1d812b79 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -6,13 +6,19 @@ on:
paths:
- "github-pages/**"
- "docs/**"
+ - "reme/config/default.yaml"
+ - "integrations/claude_code/README.md"
+ - "integrations/hermes_agent/README.md"
- "README.md"
- "README_ZH.md"
- "reme_studio/README*.md"
- "reme_studio/public/og.jpg"
- "typescript/README*.md"
+ - "typescript/docs/**"
+ - "typescript/figures/**"
- "plugins/*/README*.md"
- "benchmark/*/README*.md"
+ - "benchmark/toolmemory/gitcha.png"
- "AGENTS.md"
- ".github/workflows/deploy-docs.yml"
- ".github/workflows/_build-docs.yml"
diff --git a/.github/workflows/release-typescript.yml b/.github/workflows/release-typescript.yml
index b07a2994..4d6fd06e 100644
--- a/.github/workflows/release-typescript.yml
+++ b/.github/workflows/release-typescript.yml
@@ -2,8 +2,9 @@
# 1. Update typescript/package.json and package-lock.json to the release version and merge them.
# 2. Configure npm Trusted Publishing for agentscope-ai/ReMe and this workflow file.
# 3. Run this workflow manually with the exact package version (an optional v prefix is accepted).
-# 4. Configure ClawHub Trusted Publishing for agentscope-ai/ReMe and this workflow file.
-# 5. Use the `next` tag for prereleases and `latest` only for stable releases.
+# 4. Keep the ClawHub publication docs in typescript/docs/openclaw.md and openclaw.zh-CN.md.
+# 5. Configure ClawHub Trusted Publishing for agentscope-ai/ReMe and this workflow file.
+# 6. Use the `next` tag for prereleases and `latest` only for stable releases.
name: Release / TypeScript integrations
@@ -112,6 +113,8 @@ jobs:
- name: Pack ClawHub tarball
working-directory: typescript
run: |
+ test -f docs/openclaw.md
+ test -f docs/openclaw.zh-CN.md
mkdir -p "${RUNNER_TEMP}/reme-clawhub-package"
npm run pack:clawhub -- "${RUNNER_TEMP}/reme-clawhub-package"
@@ -171,7 +174,7 @@ jobs:
tags: ${{ inputs.npm_tag }}
source_repo: ${{ github.repository }}
source_commit: ${{ github.sha }}
- source_ref: ${{ github.ref }}
+ source_ref: ${{ github.sha }}
source_path: typescript
package_artifact_name: agentscope-ai-reme-clawhub-${{ inputs.version }}
dry_run: false
diff --git a/AGENTS.md b/AGENTS.md
index 169e2b4f..ba8ae6ac 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -46,8 +46,8 @@ and concise documentation together.
- `reme/components/service/`: local CLI, HTTP, and MCP service backends.
- `reme/components/`: agent wrappers, model adapters, stores, catalogs, graphs, indexes, clients, tokenizers, and
outbound proxies.
-- `reme/steps/`: registered job steps grouped by common, file I/O, index, evolve, cookbook, benchmark, and transfer
- concerns.
+- `reme/steps/`: registered job steps grouped by common, file I/O, index, evolve, cookbook, and transfer
+ concerns, plus shared benchmark base classes under `benchmark/`.
- `reme/utils/`: shared utilities, including service discovery, logging, web-static resolution, session I/O, token
accounting, and wikilink handling.
- `tests/unit/`: primary fast, isolated validation suite.
@@ -56,12 +56,14 @@ and concise documentation together.
`@agentscope-ai/reme_studio` npm static distribution.
- `typescript/`: the independently published `@agentscope-ai/reme` package, including the shared TypeScript client and
DeepSeek Harness and OpenClaw adapters.
-- `plugins/`: installable ReMe extensions, such as Auto Fin.
+- `plugins/`: installable ReMe extensions, including Auto Fin and LME/BEAM benchmark Steps and application presets.
- `integrations/`: adapters that connect ReMe to external agent hosts, such as Claude Code, DSH, and Hermes Agent.
- `skills/`: standalone skills; `reme_memory` calls ReMe, while other skills may use separate tools or direct-file
conventions.
- `benchmark/` and `cookbook/`: runnable evaluations and example workflows.
- `docs/`: README-linked supporting pages and figures.
+- `github-pages/`: VitePress build shell, generated-content assembly, documentation checks, and GitHub Pages output. The
+ canonical theme and guides remain under `docs/`; `.generated/` and `dist/` are disposable.
## Development Setup
@@ -195,6 +197,9 @@ Integration tests may contact real model providers, services, or agent subproces
run credentialed or externally mutating tests automatically; run them only when the task requires them and the necessary
environment has been supplied or authorized. Mock network, model, and subprocess boundaries in unit tests.
+If documentation or the documentation theme changes, run `npm test` and `npm run build` from `github-pages/`. The Job
+reference is generated from `reme/config/default.yaml`; do not edit generated pages directly.
+
## Change Guardrails
- Preserve unrelated user changes in a dirty working tree.
diff --git a/README.md b/README.md
index 23593b9f..55485d34 100644
--- a/README.md
+++ b/README.md
@@ -51,12 +51,12 @@ users retain control of the durable files.
- [2026.08] - Published [`@agentscope-ai/reme`](https://www.npmjs.com/package/@agentscope-ai/reme), providing native
ReMe memory integrations for DeepSeek Harness and OpenClaw plus a shared TypeScript HTTP client.
-- [2026.08] - Published the [ReMe blog](https://agentscope-ai.github.io/ReMe/?doc=en-reme-blog), an end-to-end introduction to its local-first memory
+- [2026.08] - Published the [ReMe blog](https://reme.agentscope.io/en/reme-blog), an end-to-end introduction to its local-first memory
architecture, self-evolving workflows, hybrid search, proactive discovery, and benchmark results.
-- [2026.08] - [Experience-driven enhancement method](https://reme.agentscope.io/?doc=toolmemory-en) of agent tool-use execution built
+- [2026.08] - [Experience-driven enhancement method](https://reme.agentscope.io/en/benchmarks/toolmemory) of agent tool-use execution built
on ReMe is available on [arXiv:2608.03403](https://arxiv.org/abs/2608.03403).
-- [2026.07] - Introduced optional plugins: [Daily Paper](https://reme.agentscope.io/?doc=daily-paper-en) for paper discovery and
- analysis, and [Auto Fin](https://reme.agentscope.io/?doc=auto-fin-en) for researching the latest 24 hours of topic-related CLS news
+- [2026.07] - Introduced optional plugins: [Daily Paper](https://reme.agentscope.io/en/plugins/daily-paper) for paper discovery and
+ analysis, and [Auto Fin](https://reme.agentscope.io/en/plugins/auto-fin) for researching the latest 24 hours of topic-related CLS news
with local-memory search and validated historical wikilinks.
- [2026.07] - Our
paper [Remember Me, Refine Me: A Dynamic Procedural Memory Framework for Experience-Driven Agent Evolution](https://aclanthology.org/2026.findings-acl.829/)
@@ -171,7 +171,7 @@ Related: [[digest/wiki/memory-as-file.md]]
The `core` installation includes Studio. After starting ReMe, open to browse, edit, and search
the workspace. To add Studio to a base installation, use `pip install "reme-ai[web]"`. See the
-[ReMe Studio guide](https://reme.agentscope.io/?doc=studio-en) for source builds, configuration, and development.
+[ReMe Studio guide](https://reme.agentscope.io/en/workspace/studio) for source builds, configuration, and development.
## 🤝 Use ReMe with Your Agent
@@ -308,12 +308,12 @@ benchmark.
| Benchmark | Setting | Sample size | Agentic score | Focus |
| --------------------------------------------------------------------------- | ------------ | -----------------------: | ------------: | ------------------------------------------------------------------ |
-| **[LongMemEval cleaned-s](https://reme.agentscope.io/?doc=longmemeval-en)** | **Overall** | **500 questions** | **89.4%** | Cross-session retrieval, knowledge updates, and temporal reasoning |
-| [BEAM](https://reme.agentscope.io/?doc=beam-en) | 100K context | 20 cases / 400 questions | 66.1% | Ten types of long-context memory tasks |
-| [BEAM](https://reme.agentscope.io/?doc=beam-en) | 1M context | 35 cases / 700 questions | 65.0% | Ultra-long conversation settings |
+| **[LongMemEval cleaned-s](https://reme.agentscope.io/en/benchmarks/longmemeval)** | **Overall** | **500 questions** | **89.4%** | Cross-session retrieval, knowledge updates, and temporal reasoning |
+| [BEAM](https://reme.agentscope.io/en/benchmarks/beam) | 100K context | 20 cases / 400 questions | 66.1% | Ten types of long-context memory tasks |
+| [BEAM](https://reme.agentscope.io/en/benchmarks/beam) | 1M context | 35 cases / 700 questions | 65.0% | Ultra-long conversation settings |
ReMe also achieved a **0.580 PROC score across five user personas** in the repository's
-[π-Bench evaluation](https://reme.agentscope.io/?doc=pibench-en), 2.4% above NanoBot under the same test-model configuration. PROC
+[π-Bench evaluation](https://reme.agentscope.io/en/benchmarks/pibench), 2.4% above NanoBot under the same test-model configuration. PROC
measures proactive handling of hidden intent, clarification, cross-session preferences and conventions, task
dependencies, and underspecified requests.
@@ -326,8 +326,8 @@ plugins; see the source distributions and their documentation for [Daily Paper](
| Plugin | Capability |
| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
-| [Daily Paper](https://reme.agentscope.io/?doc=daily-paper-en) | Discover and rank papers, analyze PDFs with an agent, and generate file-native notes and a five-minute brief. |
-| [Auto Fin](https://reme.agentscope.io/?doc=auto-fin-en) | Fetch topic-related CLS news, search ReMe history, and generate wikilink-backed Markdown reports. |
+| [Daily Paper](https://reme.agentscope.io/en/plugins/daily-paper) | Discover and rank papers, analyze PDFs with an agent, and generate file-native notes and a five-minute brief. |
+| [Auto Fin](https://reme.agentscope.io/en/plugins/auto-fin) | Fetch topic-related CLS news, search ReMe history, and generate wikilink-backed Markdown reports. |
See [Plugin Management](docs/en/plugin_management.md) to install, inspect, validate, enable, and uninstall ReMe plugins.
@@ -338,6 +338,8 @@ These guides cover the main user workflows and the runtime contracts implemented
| Guide | What you will learn |
| ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| [Quick Start](docs/en/quick_start.md) | Install ReMe, start the service, and run the first file and memory operations. |
+| [Configuration](docs/en/configuration.md) | Configure the workspace, models, Service, Jobs, Components, plugins, and CLI overrides. |
+| [Services and Deployment](docs/en/services.md) | Use HTTP, SSE, MCP, and Studio while respecting the default security boundary. |
| [Memory as File](docs/en/memory_as_file.md) | Understand workspace layers, frontmatter, wikilinks, chunks, and the file-as-source-of-truth model. |
| [Auto Memory](docs/en/auto_memory.md) | Preserve source conversations and distill reusable daily memory cards. |
| [Auto Resource](docs/en/auto_resource.md) | Import supported text and image resources as source-linked daily cards. |
@@ -347,7 +349,9 @@ These guides cover the main user workflows and the runtime contracts implemented
| [Application Scenarios](docs/en/reme_scene.md) | Follow concrete financial research, coding-memory, and personal knowledge-base examples. |
| [Framework](docs/en/framework.md) | Understand Application, Job, Step, Component, service, configuration, and lifecycle boundaries. |
| [TypeScript integrations](typescript/README.md) | Configure the shared client and native DeepSeek Harness and OpenClaw adapters. |
-| [ReMe Blog](https://agentscope-ai.github.io/ReMe/?doc=en-reme-blog) | Read the product story, design rationale, examples, and benchmark summary. |
+| [CLI and Job API](docs/en/reference/cli.md) | Learn command syntax and use the generated default Job parameter reference. |
+| [Operations and Recovery](docs/en/operations.md) | Diagnose services, maintain indexes, and back up, migrate, or recover a workspace. |
+| [ReMe Blog](https://reme.agentscope.io/en/reme-blog) | Read the product story, design rationale, examples, and benchmark summary. |
## 🛠️ Common Commands
diff --git a/README_ZH.md b/README_ZH.md
index bd7b6519..7de559a1 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -49,13 +49,13 @@
- [2026.08] - 发布 [`@agentscope-ai/reme`](https://www.npmjs.com/package/@agentscope-ai/reme),提供统一 TypeScript HTTP
client,以及 DeepSeek Harness 和 OpenClaw 的原生 ReMe 记忆集成。
-- [2026.08] - 发布 [ReMe 博客](https://agentscope-ai.github.io/ReMe/?doc=zh-reme-blog),系统介绍本地优先的记忆架构、自进化工作流、混合检索、
+- [2026.08] - 发布 [ReMe 博客](https://reme.agentscope.io/zh/reme-blog),系统介绍本地优先的记忆架构、自进化工作流、混合检索、
主动发现与评测结果。
- [2026.08] - 基于 ReMe 的智能体工具使用
- [经验驱动增强方法](https://reme.agentscope.io/?doc=toolmemory-zh)已发布,见
+ [经验驱动增强方法](https://reme.agentscope.io/zh/benchmarks/toolmemory)已发布,见
[arXiv:2608.03403](https://arxiv.org/abs/2608.03403)。
-- [2026.07] - 新增可选插件:[每日论文](https://reme.agentscope.io/?doc=daily-paper-zh)用于论文发现与解析,
- [Auto Fin](https://reme.agentscope.io/?doc=auto-fin-zh)用于研究最近 24 小时的主题相关财联社新闻,通过本地记忆搜索回顾历史材料并构建
+- [2026.07] - 新增可选插件:[每日论文](https://reme.agentscope.io/zh/plugins/daily-paper)用于论文发现与解析,
+ [Auto Fin](https://reme.agentscope.io/zh/plugins/auto-fin)用于研究最近 24 小时的主题相关财联社新闻,通过本地记忆搜索回顾历史材料并构建
wikilink。
- [2026.07] -
我们的论文 [Remember Me, Refine Me: A Dynamic Procedural Memory Framework for Experience-Driven Agent Evolution](https://aclanthology.org/2026.findings-acl.829/)
@@ -170,7 +170,7 @@ ReMe 会把 Agent 记忆保存为可读的 Markdown。
上面的 `core` 安装已包含 Studio。启动 ReMe 后,打开 即可浏览、编辑和搜索 workspace。
如需为基础安装单独添加 Studio,可使用 `pip install "reme-ai[web]"`。源码构建、配置和开发说明见
-[ReMe Studio 指南](https://reme.agentscope.io/?doc=studio-zh)。
+[ReMe Studio 指南](https://reme.agentscope.io/zh/workspace/studio)。
## 🤝 将 ReMe 接入你的 Agent
@@ -301,11 +301,11 @@ ReMe 通过 Agent 多轮搜索与读取的方式,评测多会话和超长上
| 基准 | 设置 | 样本量 | Agentic 得分 | 主要检验内容 |
| --------------------------------------------------------------------------- | ----------- | ----------------: | -----------: | ------------------------------ |
-| **[LongMemEval cleaned-s](https://reme.agentscope.io/?doc=longmemeval-zh)** | **整体** | **500 题** | **89.4%** | 跨会话检索、知识更新与时间推理 |
-| [BEAM](https://reme.agentscope.io/?doc=beam-zh) | 100K 上下文 | 20 cases / 400 题 | 66.1% | 十类长上下文记忆任务 |
-| [BEAM](https://reme.agentscope.io/?doc=beam-zh) | 1M 上下文 | 35 cases / 700 题 | 65.0% | 超长对话设置 |
+| **[LongMemEval cleaned-s](https://reme.agentscope.io/zh/benchmarks/longmemeval)** | **整体** | **500 题** | **89.4%** | 跨会话检索、知识更新与时间推理 |
+| [BEAM](https://reme.agentscope.io/zh/benchmarks/beam) | 100K 上下文 | 20 cases / 400 题 | 66.1% | 十类长上下文记忆任务 |
+| [BEAM](https://reme.agentscope.io/zh/benchmarks/beam) | 1M 上下文 | 35 cases / 700 题 | 65.0% | 超长对话设置 |
-在仓库的 [π-Bench 评测](https://reme.agentscope.io/?doc=pibench-zh)中,ReMe Agent 在 5 种用户角色上的平均 **PROC 得分为 0.580**
+在仓库的 [π-Bench 评测](https://reme.agentscope.io/zh/benchmarks/pibench)中,ReMe Agent 在 5 种用户角色上的平均 **PROC 得分为 0.580**
,比相同测试模型配置的 NanoBot 高 2.4%。PROC 用于评估隐藏意图完成、针对性澄清、跨会话偏好和规范复用、跨任务依赖推断以及欠规格请求推进等主动性能力。
## 🧩 扩展与插件
@@ -316,8 +316,8 @@ ReMe 通过 Agent 多轮搜索与读取的方式,评测多会话和超长上
| 插件 | 能力 |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------ |
-| [每日论文](https://reme.agentscope.io/?doc=daily-paper-zh) | 发现并排序论文,使用 Agent 解读 PDF,生成文件化论文笔记和五分钟简报。 |
-| [Auto Fin](https://reme.agentscope.io/?doc=auto-fin-zh) | 拉取主题相关财联社新闻,搜索 ReMe 历史材料并生成带 wikilink 的 Markdown 报告。 |
+| [每日论文](https://reme.agentscope.io/zh/plugins/daily-paper) | 发现并排序论文,使用 Agent 解读 PDF,生成文件化论文笔记和五分钟简报。 |
+| [Auto Fin](https://reme.agentscope.io/zh/plugins/auto-fin) | 拉取主题相关财联社新闻,搜索 ReMe 历史材料并生成带 wikilink 的 Markdown 报告。 |
安装、查看、校验、启用和卸载 ReMe 插件的方法见[插件管理](docs/zh/plugin_management.md)。
@@ -328,6 +328,8 @@ ReMe 通过 Agent 多轮搜索与读取的方式,评测多会话和超长上
| 文档 | 主要内容 |
| ------------------------------------------------------------------------ | ---------------------------------------------------------------------- |
| [快速开始](docs/zh/quick_start.md) | 安装 ReMe、启动服务,并执行首次文件和记忆操作。 |
+| [基础配置](docs/zh/configuration.md) | 配置 workspace、模型、Service、Job、Component、插件和命令行覆盖。 |
+| [服务与部署](docs/zh/services.md) | 使用 HTTP、SSE、MCP 和 Studio,并理解默认安全边界。 |
| [Memory as File](docs/zh/memory_as_file.md) | 理解 workspace 分层、frontmatter、wikilink、chunk 和文件事实来源模型。 |
| [Auto Memory](docs/zh/auto_memory.md) | 保留过滤后的对话来源记录,并提炼可复用的 daily 记忆卡片。 |
| [Auto Resource](docs/zh/auto_resource.md) | 导入支持的文本与图像资料,转换为可追溯来源的 daily 卡片。 |
@@ -337,7 +339,9 @@ ReMe 通过 Agent 多轮搜索与读取的方式,评测多会话和超长上
| [应用场景](docs/zh/reme_scene.md) | 查看金融研究、研发记忆和个人知识库的完整使用示例。 |
| [框架说明](docs/zh/framework.md) | 理解 Application、Job、Step、Component、service、配置和生命周期边界。 |
| [TypeScript 集成](typescript/README_ZH.md) | 配置统一 client,以及 DeepSeek Harness 和 OpenClaw 原生适配器。 |
-| [ReMe 博客](https://agentscope-ai.github.io/ReMe/?doc=zh-reme-blog) | 了解完整产品故事、设计动机、使用示例和评测摘要。 |
+| [CLI 与 Job API](docs/zh/reference/cli.md) | 查询命令语法,以及由默认配置自动生成的 Job 参数参考。 |
+| [运维与恢复](docs/zh/operations.md) | 诊断服务、维护索引,并备份、迁移和恢复 workspace。 |
+| [ReMe 博客](https://reme.agentscope.io/zh/reme-blog) | 了解完整产品故事、设计动机、使用示例和评测摘要。 |
## 🛠️ 常用命令
diff --git a/benchmark/beam/README.md b/benchmark/beam/README.md
index aef8a3b7..014427c2 100644
--- a/benchmark/beam/README.md
+++ b/benchmark/beam/README.md
@@ -15,8 +15,20 @@ include abstention, contradiction resolution, event ordering, information
extraction, instruction following, knowledge update, multi-session reasoning,
preference following, summarization, and temporal reasoning.
-> For the shared setup (dependencies, credentials, log conventions) see the
-> [top-level benchmark README](../README.md).
+Install ReMe and the BEAM plugin in editable mode from the repository root:
+
+```bash
+python -m pip install -e ".[as]"
+reme plugins install ./plugins/beam --editable
+reme plugins validate beam
+```
+
+The runner explicitly enables the installed `beam` plugin and combines its defaults with
+ReMe's built-in `benchmark` preset. Editable installation keeps changes under
+[`plugins/beam`](../../plugins/beam/README.md) visible without reinstalling the plugin.
+Custom application config paths still work through `reme.config` and can use `extends: benchmark`.
+This directory continues to own the runner, evaluation settings, dataset and outputs.
+Model credentials use the environment variables declared by the shared benchmark configuration.
## 1. Get the Dataset
@@ -59,7 +71,7 @@ python benchmark/beam/run.py --eval_only # reuse existing workspac
| `dataset.start_index` / `num_items` | Case pagination (`num_items` `0` = all). |
| `dataset.workspace_root` | Per-case workspace root (`benchmark/beam/workspaces/beam`). |
| `evaluation.num_workers` | `0` = auto, `1` = sequential, `>1` = parallel. |
-| `reme.config` | ReMe config used (`beam.yaml`). |
+| `reme.config` | ReMe config used (`benchmark`). |
| `output.dir` | Results directory (`benchmark/beam/results`). |
## 5. Outputs
diff --git a/benchmark/beam/README_ZH.md b/benchmark/beam/README_ZH.md
index f562193b..1c063497 100644
--- a/benchmark/beam/README_ZH.md
+++ b/benchmark/beam/README_ZH.md
@@ -13,7 +13,19 @@ ordering(事件排序)、information extraction(信息抽取)、instruct
knowledge update(知识更新)、multi-session reasoning(多会话推理)、preference following
(偏好遵循)、summarization(摘要)与 temporal reasoning(时间推理)。
-> 公共设置(依赖、凭据、日志约定)见[总评测说明](../README_ZH.md)。
+在仓库根目录以 editable 模式安装 ReMe 和 BEAM 插件:
+
+```bash
+python -m pip install -e ".[as]"
+reme plugins install ./plugins/beam --editable
+reme plugins validate beam
+```
+
+runner 显式启用已安装的 `beam` 插件,并将插件默认配置与 ReMe 内置的 `benchmark` 配置组合。
+editable 安装会让 [`plugins/beam`](../../plugins/beam/README_ZH.md) 下的源码修改直接生效,无需重复安装。
+本目录继续保留评测参数、数据集及输出。自定义完整应用配置路径仍可通过 `reme.config` 指定,
+并可使用 `extends: benchmark`。
+模型凭据通过公共 benchmark 配置中声明的环境变量设置。
## 1. 获取数据集
@@ -55,7 +67,7 @@ python benchmark/beam/run.py --eval_only # 复用已有工作区
| `dataset.start_index` / `num_items` | case 分页(`num_items` 为 `0` 表示全部)。 |
| `dataset.workspace_root` | case 工作区根目录(`benchmark/beam/workspaces/beam`)。 |
| `evaluation.num_workers` | `0` = 自动,`1` = 串行,`>1` = 并行。 |
-| `reme.config` | 使用的 ReMe 配置(`beam.yaml`)。 |
+| `reme.config` | 使用的 ReMe 配置(`benchmark`)。 |
| `output.dir` | 结果目录(`benchmark/beam/results`)。 |
## 5. 输出
diff --git a/benchmark/beam/config.yaml b/benchmark/beam/config.yaml
index 423697a5..cca633b8 100644
--- a/benchmark/beam/config.yaml
+++ b/benchmark/beam/config.yaml
@@ -14,7 +14,7 @@ evaluation:
compress_session: false # true = compress session chunks in search_v2 (query-aware); false = no compression
reme:
- config: "beam.yaml" # reme config (in reme/config/)
+ config: "benchmark" # shared ReMe benchmark preset; runner enables the installed beam plugin
output:
dir: "benchmark/beam/results"
diff --git a/benchmark/beam/run.py b/benchmark/beam/run.py
index 06e35b7d..b49284fd 100644
--- a/benchmark/beam/run.py
+++ b/benchmark/beam/run.py
@@ -29,7 +29,7 @@ import yaml
from dotenv import load_dotenv
# Load .env from project root
-_PROJECT_ROOT = Path(__file__).parent.parent.parent
+_PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
load_dotenv(_PROJECT_ROOT / ".env")
# Workspace root — read from config.yaml (dataset.workspace_root)
@@ -151,6 +151,22 @@ def load_eval_config(config_path: str | None = None) -> dict:
return yaml.safe_load(raw)
+def create_reme_app(config: str = "benchmark", **overrides):
+ """Create an app with the installed BEAM plugin explicitly enabled.
+
+ Plugin discovery remains environment-based; editable installation keeps local
+ plugin source changes visible to every multiprocessing worker.
+ """
+ from reme import Application
+ from reme.config import resolve_app_config
+
+ enabled_plugins = list(overrides.pop("plugins", ()) or ())
+ if "beam" not in enabled_plugins:
+ enabled_plugins.append("beam")
+ app_config = resolve_app_config(config=config, plugins=enabled_plugins, **overrides)
+ return Application(**app_config)
+
+
# ---------------------------------------------------------------------------
# BEAM data loading
# ---------------------------------------------------------------------------
@@ -319,8 +335,6 @@ async def evaluate_case(eval_config: dict, case_id: str, eval_only: bool = False
Returns:
A results dict with all questions, answers, and judgments.
"""
- from reme import Application
- from reme.config import resolve_app_config
dataset_cfg = eval_config["dataset"]
chat_size = dataset_cfg["chat_size"]
@@ -375,7 +389,7 @@ async def evaluate_case(eval_config: dict, case_id: str, eval_only: bool = False
force_init=True,
)
- cfg = resolve_app_config(
+ app = create_reme_app(
config=eval_config["reme"]["config"],
workspace_dir=workspace_dir,
log_to_console=output_cfg.get("log_to_console", True),
@@ -383,7 +397,6 @@ async def evaluate_case(eval_config: dict, case_id: str, eval_only: bool = False
enable_logo=False,
)
- app = Application(**cfg)
await app.start()
from reme.utils.evaluation_interface import check_agent_token_usage # noqa: E402
diff --git a/benchmark/longmemeval/README.md b/benchmark/longmemeval/README.md
index 070f921e..7f07d170 100644
--- a/benchmark/longmemeval/README.md
+++ b/benchmark/longmemeval/README.md
@@ -12,8 +12,20 @@ agentic (ReAct) mode, and scores the answer with an LLM-as-judge.
Question types include single-session (user / assistant / preference),
multi-session reasoning, knowledge update, and temporal reasoning.
-> For the shared setup (dependencies, credentials, log conventions) see the
-> [top-level benchmark README](../README.md).
+Install ReMe and the LongMemEval plugin in editable mode from the repository root:
+
+```bash
+python -m pip install -e ".[as]"
+reme plugins install ./plugins/lme --editable
+reme plugins validate lme
+```
+
+The runner explicitly enables the installed `lme` plugin and combines its defaults with
+ReMe's built-in `benchmark` preset. Editable installation keeps changes under
+[`plugins/lme`](../../plugins/lme/README.md) visible without reinstalling the plugin.
+Custom application config paths still work through `reme.config` and can use `extends: benchmark`.
+This directory continues to own the runner, evaluation settings, dataset and outputs.
+Model credentials use the environment variables declared by the shared benchmark configuration.
## 1. Get the Dataset
@@ -46,7 +58,8 @@ python benchmark/longmemeval/run.py --eval_only # reuse existing w
1. Load the dataset (ground truth is embedded in the data file).
2. For each item, create an isolated workspace and ingest sessions in chronological order.
-3. Trigger `auto_dream` when consecutive sessions cross the configured hour (default 23:00).
+3. If a custom application configuration enables `auto_dream`, trigger it when sessions cross the configured hour
+ (default 23:00). The packaged preset leaves it disabled.
4. Answer each question via agentic (ReAct) mode.
5. Judge the answer (binary yes/no) with the `answer_judge` job and print per-type accuracy.
@@ -60,7 +73,7 @@ python benchmark/longmemeval/run.py --eval_only # reuse existing w
| `dataset.workspace_root` | Per-item workspace root (`benchmark/longmemeval/workspaces/longmemeval-s`). |
| `evaluation.num_workers` | `0` = auto (cpu-2), `1` = sequential, `>1` = parallel. |
| `evaluation.filter_future_sessions` | Only ingest sessions with timestamp ≤ `question_date`. |
-| `reme.config` | ReMe config used (`lme.yaml`). |
+| `reme.config` | ReMe config used (`benchmark`). |
| `reme.dream_trigger_hour` / `dream_scan_days` / `dream_max_units` | Dream triggering behavior. |
| `output.dir` | Results directory (`benchmark/longmemeval/results`). |
@@ -93,4 +106,4 @@ agentscope==2.0.4.post1, conda reme env, 32 workers, eval-only (reusing prebuilt
| single-session-preference | 0.633 | 36,802 | 818 | 37,620 | 3.60 |
| single-session-user | 0.986 | 27,433 | 359 | 27,792 | 2.60 |
| temporal-reasoning | 0.902 | 62,674 | 985 | 63,659 | 4.97 |
-| **OVERALL** | **0.894** | **43,448** | **876** | **44,324** | **3.69** |
\ No newline at end of file
+| **OVERALL** | **0.894** | **43,448** | **876** | **44,324** | **3.69** |
diff --git a/benchmark/longmemeval/README_ZH.md b/benchmark/longmemeval/README_ZH.md
index 9c07b175..4c66df67 100644
--- a/benchmark/longmemeval/README_ZH.md
+++ b/benchmark/longmemeval/README_ZH.md
@@ -8,7 +8,19 @@ LongMemEval 是一个面向**多轮多会话历史的长期记忆能力**的评
题型包括单会话(user / assistant / preference)、多会话推理、知识更新与时间推理等。
-> 公共设置(依赖、凭据、日志约定)见[总评测说明](../README_ZH.md)。
+在仓库根目录以 editable 模式安装 ReMe 和 LongMemEval 插件:
+
+```bash
+python -m pip install -e ".[as]"
+reme plugins install ./plugins/lme --editable
+reme plugins validate lme
+```
+
+runner 显式启用已安装的 `lme` 插件,并将插件默认配置与 ReMe 内置的 `benchmark` 配置组合。
+editable 安装会让 [`plugins/lme`](../../plugins/lme/README_ZH.md) 下的源码修改直接生效,无需重复安装。
+本目录继续保留评测参数、数据集及输出。自定义完整应用配置路径仍可通过 `reme.config` 指定,
+并可使用 `extends: benchmark`。
+模型凭据通过公共 benchmark 配置中声明的环境变量设置。
## 1. 获取数据集
@@ -41,7 +53,7 @@ python benchmark/longmemeval/run.py --eval_only # 复用已有工
1. 加载数据集(ground truth 已内嵌在数据文件中)。
2. 为每个条目创建独立工作区,按时间顺序摄入会话。
-3. 当相邻会话跨越配置的时刻(默认 23:00)时触发 `auto_dream`。
+3. 若自定义应用配置启用了 `auto_dream`,在相邻会话跨越配置时刻(默认 23:00)时触发;插件预设保持关闭。
4. 以 agentic(ReAct)模式回答每个问题。
5. 通过 `answer_judge` 任务对答案做二元(yes/no)评判,并输出各类型准确率。
@@ -55,7 +67,7 @@ python benchmark/longmemeval/run.py --eval_only # 复用已有工
| `dataset.workspace_root` | 条目工作区根目录(`benchmark/longmemeval/workspaces/longmemeval-s`)。 |
| `evaluation.num_workers` | `0` = 自动(cpu-2),`1` = 串行,`>1` = 并行。 |
| `evaluation.filter_future_sessions` | 仅摄入时间戳 ≤ `question_date` 的会话。 |
-| `reme.config` | 使用的 ReMe 配置(`lme.yaml`)。 |
+| `reme.config` | 使用的 ReMe 配置(`benchmark`)。 |
| `reme.dream_trigger_hour` / `dream_scan_days` / `dream_max_units` | dream 触发行为。 |
| `output.dir` | 结果目录(`benchmark/longmemeval/results`)。 |
diff --git a/benchmark/longmemeval/config.yaml b/benchmark/longmemeval/config.yaml
index 72ee7b75..36260479 100644
--- a/benchmark/longmemeval/config.yaml
+++ b/benchmark/longmemeval/config.yaml
@@ -10,7 +10,7 @@ dataset:
workspace_root: "benchmark/longmemeval/workspaces/longmemeval-s" # workspace root for item workspaces
evaluation:
- # LLM-as-judge uses the 'judge' as_llm component defined in lme.yaml
+ # LLM-as-judge uses the 'judge' as_llm component defined in benchmark.yaml
# Model and credentials are configured there (reading from .env)
# Judgment is always binary (yes/no) — defined in lme/llm_judge.yaml
num_workers: 32 # 0 = auto (cpu_count - 2, min 1); 1 = sequential; >1 = parallel
@@ -18,7 +18,7 @@ evaluation:
compress_session: false # true = compress session chunks in search_v2 (query-aware); false = no compression
reme:
- config: "lme.yaml" # reme config to use (in reme/config/)
+ config: "benchmark" # shared ReMe benchmark preset; runner enables the installed lme plugin
# Dream trigger: when gap between consecutive sessions crosses this hour (23:00)
dream_trigger_hour: 23
# Dream scan_days for each trigger
diff --git a/benchmark/longmemeval/run.py b/benchmark/longmemeval/run.py
index 9e48af43..97b10ae4 100644
--- a/benchmark/longmemeval/run.py
+++ b/benchmark/longmemeval/run.py
@@ -28,7 +28,7 @@ import yaml
from dotenv import load_dotenv
# Load .env from project root
-_PROJECT_ROOT = Path(__file__).parent.parent.parent
+_PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
load_dotenv(_PROJECT_ROOT / ".env")
# Workspace root for evaluation items — read from config.yaml (dataset.workspace_root)
@@ -150,6 +150,22 @@ def load_eval_config(config_path: str | None = None) -> dict:
return yaml.safe_load(raw)
+def create_reme_app(config: str = "benchmark", **overrides):
+ """Create an app with the installed LongMemEval plugin explicitly enabled.
+
+ Plugin discovery remains environment-based; editable installation keeps local
+ plugin source changes visible to every multiprocessing worker.
+ """
+ from reme import Application
+ from reme.config import resolve_app_config
+
+ enabled_plugins = list(overrides.pop("plugins", ()) or ())
+ if "lme" not in enabled_plugins:
+ enabled_plugins.append("lme")
+ app_config = resolve_app_config(config=config, plugins=enabled_plugins, **overrides)
+ return Application(**app_config)
+
+
# ---------------------------------------------------------------------------
# Date utilities
# ---------------------------------------------------------------------------
@@ -257,8 +273,6 @@ async def evaluate_item(item: dict, eval_config: dict, item_index: int, eval_onl
using the existing workspace. Useful for re-evaluating different query
configurations without re-ingesting sessions.
"""
- from reme import Application
- from reme.config import resolve_app_config
from reme.utils.evaluation_interface import track_agent_token_usage, track_job_counts
reme_cfg = eval_config["reme"]
@@ -325,7 +339,7 @@ async def evaluate_item(item: dict, eval_config: dict, item_index: int, eval_onl
force_init=True,
)
- cfg = resolve_app_config(
+ app = create_reme_app(
config=reme_cfg["config"],
workspace_dir=workspace_dir,
log_to_console=output_cfg.get("log_to_console", True),
@@ -333,7 +347,6 @@ async def evaluate_item(item: dict, eval_config: dict, item_index: int, eval_onl
enable_logo=False,
)
- app = Application(**cfg)
await app.start()
try:
diff --git a/benchmark/pibench/README.md b/benchmark/pibench/README.md
index c62249a0..858e689c 100644
--- a/benchmark/pibench/README.md
+++ b/benchmark/pibench/README.md
@@ -289,7 +289,7 @@ removed"). They need the executed tool calls in the trace. The pipeline:
| user_agent / judger models | `config/models/reme.yaml` |
| Agent system prompt | `bridge_reme.py` `build_system_prompt()` |
| Memory retrieval limit/threshold | `--search-limit/--search-min-score` on the bridge command in `run_persona.sh` |
-| ReMe internal parameters | **Do not modify ReMe source**; write a dedicated config modeled on `reme/config/beam.yaml` and override via `resolve_app_config(config=...)` (see bridge `_init_reme_app`) |
+| ReMe internal parameters | **Do not modify ReMe source**; extend the built-in `benchmark` config and override via `resolve_app_config(config=...)` (see bridge `_init_reme_app`) |
| Turn timeout / tool iteration cap | `config/models/reme.yaml` `run.turn_timeout`, `model.max_tool_iterations` |
## 11. Troubleshooting
diff --git a/benchmark/pibench/README_ZH.md b/benchmark/pibench/README_ZH.md
index 0a8b58d6..5e4668b2 100644
--- a/benchmark/pibench/README_ZH.md
+++ b/benchmark/pibench/README_ZH.md
@@ -255,7 +255,7 @@ grep -h "overall_average_score\|overall_proactiveness" \
| user_agent / judger 模型 | `config/models/reme.yaml` |
| agent system prompt | `bridge_reme.py` `build_system_prompt()` |
| 记忆检索条数/阈值 | `run_persona.sh` bridge 启动命令的 `--search-limit/--search-min-score` |
-| ReMe 内部参数 | **不要改 ReMe 源码**;仿照 `reme/config/beam.yaml` 写专有配置,经 `resolve_app_config(config=...)` 覆盖(见 bridge `_init_reme_app`) |
+| ReMe 内部参数 | **不要改 ReMe 源码**;继承内置 `benchmark` 配置,并经 `resolve_app_config(config=...)` 覆盖(见 bridge `_init_reme_app`) |
| 轮超时/工具迭代上限 | `config/models/reme.yaml` `run.turn_timeout`、`model.max_tool_iterations` |
## 11. 故障排查
diff --git a/benchmark/toolmemory/README.md b/benchmark/toolmemory/README.md
index 7f358018..40ac47fe 100644
--- a/benchmark/toolmemory/README.md
+++ b/benchmark/toolmemory/README.md
@@ -6,7 +6,7 @@
> Code: [https://github.com/WangCan1178/ExpG](https://github.com/WangCan1178/ExpG)
-
+
### Overview
diff --git a/benchmark/toolmemory/README_ZH.md b/benchmark/toolmemory/README_ZH.md
index b0c5b89b..0d247385 100644
--- a/benchmark/toolmemory/README_ZH.md
+++ b/benchmark/toolmemory/README_ZH.md
@@ -6,7 +6,7 @@
> 代码:[https://github.com/WangCan1178/ExpG](https://github.com/WangCan1178/ExpG)
-
+
### 简介
diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts
new file mode 100644
index 00000000..697aea53
--- /dev/null
+++ b/docs/.vitepress/config.mts
@@ -0,0 +1,344 @@
+import fs from "node:fs";
+import path from "node:path";
+import { execFileSync } from "node:child_process";
+import { fileURLToPath } from "node:url";
+import { defineConfig, type DefaultTheme } from "vitepress";
+import { legacyRoutes } from "./legacy-routes.mjs";
+
+const sourceRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
+const repositoryRoot = path.resolve(sourceRoot, "../../..");
+const repository = "https://github.com/agentscope-ai/ReMe";
+const base = process.env.DOCS_BASE || "/";
+
+function readSourceMap(): Record {
+ try {
+ return JSON.parse(fs.readFileSync(path.join(sourceRoot, ".source-map.json"), "utf8"));
+ } catch {
+ return {};
+ }
+}
+
+const sourceMap = readSourceMap();
+
+function collectMarkdown(directory: string, root = directory): string[] {
+ const files: string[] = [];
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
+ if (entry.name.startsWith(".") || entry.name === "public" || entry.name === "figure") continue;
+ const absolute = path.join(directory, entry.name);
+ if (entry.isDirectory()) files.push(...collectMarkdown(absolute, root));
+ else if (entry.name.endsWith(".md")) files.push(path.relative(root, absolute).replaceAll(path.sep, "/"));
+ }
+ return files.sort();
+}
+
+function buildLlmsFiles(outDir: string) {
+ const pages = collectMarkdown(sourceRoot);
+ const index = [
+ "# ReMe Documentation",
+ "",
+ "> Local-first, file-native memory for agents.",
+ "",
+ ...pages.map((relativePath) => {
+ const source = fs.readFileSync(path.join(sourceRoot, relativePath), "utf8");
+ const title = source.match(/^#\s+(.+)$/m)?.[1]
+ || source.match(/^title:\s*(.+)$/m)?.[1]
+ || path.basename(relativePath, ".md");
+ const route = relativePath.replace(/(?:^|\/)index\.md$/, "").replace(/\.md$/, "");
+ return `- [${title}](https://reme.agentscope.io/${route})`;
+ }),
+ "",
+ ];
+ fs.writeFileSync(path.join(outDir, "llms.txt"), index.join("\n"), "utf8");
+
+ const full = ["# ReMe Documentation", ""];
+ for (const relativePath of pages) {
+ const source = fs.readFileSync(path.join(sourceRoot, relativePath), "utf8");
+ full.push(``, "", source, "", "---", "");
+ const pageDir = path.join(outDir, relativePath.replace(/\.md$/, ""));
+ fs.mkdirSync(pageDir, { recursive: true });
+ fs.writeFileSync(path.join(pageDir, "llms.txt"), source, "utf8");
+ }
+ fs.writeFileSync(path.join(outDir, "llms-full.txt"), full.join("\n"), "utf8");
+}
+
+function sourcePathFor(relativePath: string) {
+ return sourceMap[relativePath] || `docs/${relativePath}`;
+}
+
+function sourceLastUpdated(relativePath: string): number | undefined {
+ const sourcePath = sourcePathFor(relativePath);
+ try {
+ const timestamp = execFileSync("git", ["log", "-1", "--format=%ct", "--", sourcePath], {
+ cwd: repositoryRoot,
+ encoding: "utf8",
+ }).trim();
+ if (timestamp) return Number(timestamp) * 1000;
+ } catch {
+ // Fall back to the canonical file timestamp outside a Git checkout.
+ }
+ try {
+ return fs.statSync(path.join(repositoryRoot, sourcePath)).mtimeMs;
+ } catch {
+ return undefined;
+ }
+}
+
+const legacyRedirectScript = `(() => {
+ const routes = ${JSON.stringify(legacyRoutes)};
+ const id = new URLSearchParams(window.location.search).get("doc");
+ const target = id && routes[id];
+ const base = ${JSON.stringify(base)};
+ if (target) {
+ const destination = /^https?:/.test(target)
+ ? target
+ : base.replace(/\\/$/, "") + target;
+ window.location.replace(destination + window.location.hash);
+ return;
+ }
+ const root = base.endsWith("/") ? base : base + "/";
+ if (window.location.pathname === root) {
+ window.location.replace(root + "zh/" + window.location.hash);
+ }
+})();`;
+
+function nav(language: "zh" | "en"): DefaultTheme.NavItem[] {
+ const zh = language === "zh";
+ return [
+ { text: zh ? "开始使用" : "Get Started", link: `/${language}/quick_start` },
+ { text: zh ? "核心概念" : "Concepts", link: `/${language}/memory_as_file` },
+ { text: zh ? "指南" : "Guides", link: `/${language}/auto_memory` },
+ { text: zh ? "集成" : "Integrations", link: `/${language}/integrations` },
+ { text: zh ? "API 参考" : "API Reference", link: `/${language}/reference/cli` },
+ { text: zh ? "常见问题" : "FAQ", link: `/${language}/faq` },
+ ];
+}
+
+function sidebar(language: "zh" | "en"): DefaultTheme.SidebarItem[] {
+ const zh = language === "zh";
+ return [
+ {
+ text: zh ? "开始使用" : "Get Started",
+ collapsed: false,
+ items: [
+ { text: zh ? "项目介绍" : "Introduction", link: `/${language}/` },
+ { text: zh ? "快速开始" : "Quick Start", link: `/${language}/quick_start` },
+ { text: zh ? "基础配置" : "Configuration", link: `/${language}/configuration` },
+ { text: zh ? "服务与部署" : "Services and Deployment", link: `/${language}/services` },
+ ],
+ },
+ {
+ text: zh ? "核心概念" : "Core Concepts",
+ collapsed: false,
+ items: [
+ { text: zh ? "文件即记忆" : "Memory as File", link: `/${language}/memory_as_file` },
+ { text: zh ? "记忆检索" : "Memory Search", link: `/${language}/memory_search` },
+ { text: zh ? "自动关联" : "Auto Link", link: `/${language}/auto_link` },
+ { text: zh ? "应用场景" : "Application Scenarios", link: `/${language}/reme_scene` },
+ ],
+ },
+ {
+ text: zh ? "记忆工作流" : "Memory Workflows",
+ collapsed: false,
+ items: [
+ { text: "Auto Memory", link: `/${language}/auto_memory` },
+ { text: "Auto Resource", link: `/${language}/auto_resource` },
+ { text: "Auto Dream", link: `/${language}/auto_dream` },
+ { text: "Proactive", link: `/${language}/proactive` },
+ ],
+ },
+ {
+ text: zh ? "Agent 集成" : "Agent Integrations",
+ collapsed: true,
+ items: [
+ { text: zh ? "集成总览" : "Overview", link: `/${language}/integrations` },
+ { text: "Claude Code", link: `/${language}/integrations/claude-code` },
+ { text: "Hermes Agent", link: `/${language}/integrations/hermes` },
+ { text: zh ? "TypeScript 客户端" : "TypeScript Client", link: `/${language}/integrations/typescript` },
+ { text: "DeepSeek Harness", link: `/${language}/integrations/dsh` },
+ { text: "OpenClaw", link: `/${language}/integrations/openclaw` },
+ ],
+ },
+ {
+ text: zh ? "工作区与插件" : "Workspace and Plugins",
+ collapsed: true,
+ items: [
+ { text: "ReMe Studio", link: `/${language}/workspace/studio` },
+ { text: zh ? "插件管理" : "Plugin Management", link: `/${language}/plugin_management` },
+ { text: zh ? "插件开发" : "Plugin Development", link: `/${language}/plugin_development` },
+ { text: zh ? "每日论文" : "Daily Paper", link: `/${language}/plugins/daily-paper` },
+ { text: "Auto Fin", link: `/${language}/plugins/auto-fin` },
+ { text: "LME", link: `/${language}/plugins/lme` },
+ { text: "BEAM", link: `/${language}/plugins/beam` },
+ ],
+ },
+ {
+ text: zh ? "API 参考" : "API Reference",
+ collapsed: true,
+ items: [
+ { text: "CLI", link: `/${language}/reference/cli` },
+ { text: zh ? "Job API" : "Job API", link: `/${language}/reference/jobs` },
+ { text: "HTTP / MCP", link: `/${language}/services#http-api` },
+ ],
+ },
+ {
+ text: zh ? "运维" : "Operations",
+ collapsed: true,
+ items: [
+ { text: zh ? "诊断、备份与恢复" : "Diagnostics, Backup, and Recovery", link: `/${language}/operations` },
+ { text: zh ? "常见问题" : "FAQ", link: `/${language}/faq` },
+ ],
+ },
+ {
+ text: zh ? "开发者" : "Development",
+ collapsed: true,
+ items: [
+ { text: zh ? "代码框架" : "Framework", link: `/${language}/framework` },
+ { text: zh ? "开源与贡献" : "Contributing", link: `/${language}/contributing` },
+ ],
+ },
+ {
+ text: zh ? "评测" : "Benchmarks",
+ collapsed: true,
+ items: [
+ { text: "BEAM", link: `/${language}/benchmarks/beam` },
+ { text: "LongMemEval", link: `/${language}/benchmarks/longmemeval` },
+ { text: "π-Bench", link: `/${language}/benchmarks/pibench` },
+ { text: "Tool Memory / ExpG", link: `/${language}/benchmarks/toolmemory` },
+ ],
+ },
+ ];
+}
+
+function configureRepositoryLinks(md: any) {
+ for (const ruleName of ["link_open", "image"] as const) {
+ const original = md.renderer.rules[ruleName];
+ md.renderer.rules[ruleName] = (tokens: any[], index: number, options: any, env: any, self: any) => {
+ const attribute = ruleName === "image" ? "src" : "href";
+ const token = tokens[index];
+ const attributeIndex = token.attrIndex(attribute);
+ const target = attributeIndex >= 0 ? token.attrs[attributeIndex][1] : "";
+ if (target && !/^(?:[a-z]+:|#|\/)/i.test(target)) {
+ const cleanTarget = target.split("#")[0].split("?")[0];
+ const generatedTarget = path.resolve(sourceRoot, path.dirname(env.relativePath), cleanTarget);
+ if (!fs.existsSync(generatedTarget)) {
+ const originalPage = sourcePathFor(env.relativePath);
+ const originalTarget = path.posix.normalize(path.posix.join(path.posix.dirname(originalPage), cleanTarget));
+ const suffix = target.slice(cleanTarget.length);
+ const url = ruleName === "image"
+ ? `https://raw.githubusercontent.com/agentscope-ai/ReMe/main/${originalTarget}${suffix}`
+ : `${repository}/blob/main/${originalTarget}${suffix}`;
+ token.attrs[attributeIndex][1] = url;
+ }
+ }
+ return original ? original(tokens, index, options, env, self) : self.renderToken(tokens, index, options);
+ };
+ }
+}
+
+export default defineConfig({
+ lang: "zh-CN",
+ title: "ReMe",
+ description: "Local-first, file-native memory for agents",
+ base,
+ cleanUrls: true,
+ lastUpdated: true,
+ ignoreDeadLinks: [/^http:\/\/localhost(?::\d+)?(?:\/|$)/],
+ sitemap: {
+ hostname: "https://reme.agentscope.io",
+ transformItems(items) {
+ const isRoot = (url: string) => url.replace(/^\/+|\/+$/g, "") === "";
+ return items.filter((item) => !isRoot(item.url)).map((item) => {
+ const route = item.url.replace(/^\/+/, "");
+ const relativePath = !route || route.endsWith("/") ? `${route}index.md` : `${route}.md`;
+ const links = item.links?.filter((link) => !isRoot(link.url));
+ return { ...item, links, lastmod: sourceLastUpdated(relativePath) };
+ });
+ },
+ },
+ head: [
+ ["link", { rel: "icon", type: "image/svg+xml", href: `${base}reme-icon.svg` }],
+ ["meta", { name: "theme-color", content: "#087f6a" }],
+ ["script", {}, legacyRedirectScript],
+ ],
+ markdown: {
+ config: configureRepositoryLinks,
+ },
+ transformPageData(pageData, { siteConfig }) {
+ const sourcePath = path.join(siteConfig.srcDir, pageData.relativePath);
+ pageData.frontmatter._sourcePath = sourcePathFor(pageData.relativePath);
+ pageData.lastUpdated = sourceLastUpdated(pageData.relativePath);
+ try {
+ pageData.frontmatter._rawMarkdown = fs.readFileSync(sourcePath, "utf8");
+ } catch {
+ pageData.frontmatter._rawMarkdown = "";
+ }
+ },
+ buildEnd(siteConfig) {
+ buildLlmsFiles(siteConfig.outDir);
+ },
+ themeConfig: {
+ logo: "/reme-icon.svg",
+ siteTitle: "ReMe",
+ nav: [
+ ...nav("zh"),
+ {
+ text: "语言",
+ items: [
+ { text: "简体中文", link: "/zh/" },
+ { text: "English", link: "/en/" },
+ ],
+ },
+ ],
+ outline: { label: "页面导航", level: [2, 3] },
+ search: {
+ provider: "local",
+ options: {
+ locales: {
+ zh: {
+ translations: {
+ button: { buttonText: "搜索文档", buttonAriaLabel: "搜索文档" },
+ modal: {
+ noResultsText: "没有找到相关内容",
+ resetButtonTitle: "清除查询",
+ footer: { selectText: "选择", navigateText: "切换", closeText: "关闭" },
+ },
+ },
+ },
+ },
+ },
+ },
+ socialLinks: [{ icon: "github", link: repository }],
+ footer: {
+ message: "Released under the Apache-2.0 License.",
+ copyright: "Copyright ReMe contributors",
+ },
+ },
+ locales: {
+ zh: {
+ label: "简体中文",
+ lang: "zh-CN",
+ link: "/zh/",
+ themeConfig: {
+ nav: nav("zh"),
+ sidebar: { "/zh/": sidebar("zh") },
+ outline: { label: "页面导航", level: [2, 3] },
+ docFooter: { prev: "上一页", next: "下一页" },
+ darkModeSwitchLabel: "外观",
+ sidebarMenuLabel: "菜单",
+ returnToTopLabel: "返回顶部",
+ langMenuLabel: "切换语言",
+ },
+ },
+ en: {
+ label: "English",
+ lang: "en-US",
+ link: "/en/",
+ themeConfig: {
+ nav: nav("en"),
+ sidebar: { "/en/": sidebar("en") },
+ outline: { label: "On this page", level: [2, 3] },
+ docFooter: { prev: "Previous page", next: "Next page" },
+ },
+ },
+ },
+});
diff --git a/docs/.vitepress/legacy-routes.mjs b/docs/.vitepress/legacy-routes.mjs
new file mode 100644
index 00000000..bdf3607e
--- /dev/null
+++ b/docs/.vitepress/legacy-routes.mjs
@@ -0,0 +1,47 @@
+export const legacyRoutes = {
+ "readme-zh": "/zh/",
+ "readme-en": "/en/",
+ "zh-quick_start": "/zh/quick_start",
+ "en-quick_start": "/en/quick_start",
+ "zh-plugin_management": "/zh/plugin_management",
+ "en-plugin_management": "/en/plugin_management",
+ "zh-memory_as_file": "/zh/memory_as_file",
+ "en-memory_as_file": "/en/memory_as_file",
+ "zh-memory_search": "/zh/memory_search",
+ "en-memory_search": "/en/memory_search",
+ "zh-auto_memory": "/zh/auto_memory",
+ "en-auto_memory": "/en/auto_memory",
+ "zh-auto_resource": "/zh/auto_resource",
+ "en-auto_resource": "/en/auto_resource",
+ "zh-auto_link": "/zh/auto_link",
+ "en-auto_link": "/en/auto_link",
+ "zh-auto_dream": "/zh/auto_dream",
+ "en-auto_dream": "/en/auto_dream",
+ "zh-proactive": "/zh/proactive",
+ "en-proactive": "/en/proactive",
+ "zh-reme_scene": "/zh/reme_scene",
+ "en-reme_scene": "/en/reme_scene",
+ "zh-framework": "/zh/framework",
+ "en-framework": "/en/framework",
+ "zh-reme-blog": "/zh/reme-blog",
+ "en-reme-blog": "/en/reme-blog",
+ "zh-contributing": "/zh/contributing",
+ "en-contributing": "/en/contributing",
+ "typescript-zh": "/zh/integrations/typescript",
+ "typescript-en": "/en/integrations/typescript",
+ "studio-zh": "/zh/workspace/studio",
+ "studio-en": "/en/workspace/studio",
+ "daily-paper-zh": "/zh/plugins/daily-paper",
+ "daily-paper-en": "/en/plugins/daily-paper",
+ "auto-fin-zh": "/zh/plugins/auto-fin",
+ "auto-fin-en": "/en/plugins/auto-fin",
+ "beam-zh": "/zh/benchmarks/beam",
+ "beam-en": "/en/benchmarks/beam",
+ "longmemeval-zh": "/zh/benchmarks/longmemeval",
+ "longmemeval-en": "/en/benchmarks/longmemeval",
+ "pibench-zh": "/zh/benchmarks/pibench",
+ "pibench-en": "/en/benchmarks/pibench",
+ "toolmemory-zh": "/zh/benchmarks/toolmemory",
+ "toolmemory-en": "/en/benchmarks/toolmemory",
+ "agents-guide": "https://github.com/agentscope-ai/ReMe/blob/main/AGENTS.md",
+};
diff --git a/docs/.vitepress/theme/CopyMarkdownButton.vue b/docs/.vitepress/theme/CopyMarkdownButton.vue
new file mode 100644
index 00000000..b3805638
--- /dev/null
+++ b/docs/.vitepress/theme/CopyMarkdownButton.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
diff --git a/docs/.vitepress/theme/SourceLink.vue b/docs/.vitepress/theme/SourceLink.vue
new file mode 100644
index 00000000..cebc353e
--- /dev/null
+++ b/docs/.vitepress/theme/SourceLink.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/docs/.vitepress/theme/custom.css b/docs/.vitepress/theme/custom.css
new file mode 100644
index 00000000..9f966ef5
--- /dev/null
+++ b/docs/.vitepress/theme/custom.css
@@ -0,0 +1,336 @@
+:root {
+ --vp-layout-max-width: 1560px;
+ --vp-c-brand-1: #087f6a;
+ --vp-c-brand-2: #086554;
+ --vp-c-brand-3: #19a98f;
+ --vp-c-brand-soft: rgba(8, 127, 106, 0.14);
+ --vp-c-bg: #ffffff;
+ --vp-c-bg-alt: #f4f7f5;
+ --vp-c-bg-elv: #ffffff;
+ --vp-c-bg-soft: #f1f6f3;
+ --vp-c-text-1: #17221d;
+ --vp-c-text-2: #526159;
+ --vp-c-text-3: #718078;
+ --vp-c-divider: #dce5e0;
+ --vp-font-family-base: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ --vp-font-family-mono: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
+ --reme-blue: #3156d9;
+ --reme-green: #087f6a;
+}
+
+.dark {
+ --vp-c-brand-1: #57dfc3;
+ --vp-c-brand-2: #35c6a9;
+ --vp-c-brand-3: #087f6a;
+ --vp-c-brand-soft: rgba(87, 223, 195, 0.14);
+ --vp-c-bg: #0d1512;
+ --vp-c-bg-alt: #09100d;
+ --vp-c-bg-elv: #14201b;
+ --vp-c-bg-soft: #17251f;
+ --vp-c-text-1: #edf7f3;
+ --vp-c-text-2: #bacbc4;
+ --vp-c-text-3: #91a49c;
+ --vp-c-divider: #283a33;
+}
+
+body {
+ background:
+ radial-gradient(circle at 8% 8%, rgba(25, 201, 176, 0.055), transparent 28rem),
+ var(--vp-c-bg);
+}
+
+.VPNav {
+ border-bottom: 1px solid color-mix(in srgb, var(--vp-c-divider) 84%, transparent);
+ background: color-mix(in srgb, var(--vp-c-bg) 88%, transparent);
+ backdrop-filter: blur(18px) saturate(140%);
+}
+
+.VPNavBarTitle .logo {
+ width: 30px;
+ height: 30px;
+}
+
+.VPNavBarTitle .title {
+ font-weight: 780;
+ letter-spacing: -0.02em;
+}
+
+.VPNavBarSearch .DocSearch-Button,
+.VPNavBarSearch button {
+ min-width: 190px;
+ border: 1px solid var(--vp-c-divider);
+ border-radius: 10px;
+ background: var(--vp-c-bg-alt);
+}
+
+.VPSidebar {
+ border-right: 1px solid var(--vp-c-divider);
+ background: color-mix(in srgb, var(--vp-c-bg-alt) 82%, transparent);
+}
+
+.VPSidebarItem .text {
+ font-size: 14px;
+}
+
+.VPSidebarItem.level-0 > .item > .text {
+ color: var(--vp-c-text-1);
+ font-weight: 750;
+}
+
+.VPSidebarItem.is-active > .item .link > .text {
+ color: var(--vp-c-brand-1);
+}
+
+.VPDocAsideOutline {
+ border-left-color: var(--vp-c-divider);
+}
+
+.VPDoc .container > .content {
+ min-width: 0;
+}
+
+.VPDoc .content-container {
+ max-width: 900px !important;
+}
+
+.vp-doc {
+ color: var(--vp-c-text-1);
+ font-size: 16px;
+ line-height: 1.78;
+}
+
+.vp-doc h1 {
+ margin-bottom: 28px;
+ font-size: clamp(36px, 5vw, 52px);
+ line-height: 1.08;
+ letter-spacing: -0.045em;
+}
+
+.vp-doc h2 {
+ margin-top: 52px;
+ border-top-color: var(--vp-c-divider);
+ font-size: 27px;
+ letter-spacing: -0.025em;
+}
+
+.vp-doc h3 {
+ margin-top: 34px;
+ font-size: 20px;
+}
+
+.vp-doc :not(pre) > code {
+ border-radius: 5px;
+ color: color-mix(in srgb, var(--vp-c-brand-1) 82%, var(--vp-c-text-1));
+}
+
+.vp-doc div[class*="language-"] {
+ border: 1px solid var(--vp-c-divider);
+ border-radius: 12px;
+ box-shadow: inset 3px 0 0 color-mix(in srgb, var(--vp-c-brand-1) 62%, transparent);
+}
+
+.copy-markdown-wrap {
+ display: flex;
+ justify-content: flex-end;
+ margin-bottom: 18px;
+}
+
+.copy-markdown {
+ display: inline-flex;
+ align-items: center;
+ gap: 7px;
+ min-height: 34px;
+ padding: 6px 12px;
+ border: 1px solid var(--vp-c-divider);
+ border-radius: 9px;
+ color: var(--vp-c-text-2);
+ background: var(--vp-c-bg-soft);
+ cursor: pointer;
+ font-size: 13px;
+ font-weight: 650;
+}
+
+.copy-markdown:hover,
+.copy-markdown.copied {
+ border-color: var(--vp-c-brand-1);
+ color: var(--vp-c-brand-1);
+}
+
+.copy-markdown svg {
+ width: 15px;
+ height: 15px;
+ fill: none;
+ stroke: currentColor;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 2;
+}
+
+.source-link-wrap {
+ margin-top: 48px;
+ padding-top: 20px;
+ border-top: 1px solid var(--vp-c-divider);
+ font-size: 13px;
+}
+
+.source-link-wrap a {
+ color: var(--vp-c-text-3);
+ text-decoration: none;
+}
+
+.source-link-wrap a:hover {
+ color: var(--vp-c-brand-1);
+}
+
+.VPHome {
+ background:
+ radial-gradient(circle at 14% 14%, rgba(25, 201, 176, 0.14), transparent 30rem),
+ radial-gradient(circle at 86% 10%, rgba(49, 86, 217, 0.11), transparent 28rem);
+}
+
+.VPHero .name {
+ background: linear-gradient(120deg, var(--reme-green), var(--reme-blue));
+ background-clip: text;
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
+
+.VPHero .name,
+.VPHero .text {
+ letter-spacing: -0.05em;
+}
+
+.VPHero .image-bg {
+ width: min(88%, 420px);
+ height: 220px;
+ border-radius: 42%;
+ background: linear-gradient(125deg, rgba(25, 201, 176, 0.34), rgba(49, 86, 217, 0.25));
+ filter: blur(52px);
+}
+
+.VPHero .image-container {
+ isolation: isolate;
+ perspective: 900px;
+}
+
+.VPHero .image-container::before,
+.VPHero .image-container::after {
+ position: absolute;
+ content: "";
+ pointer-events: none;
+}
+
+.VPHero .image-container::before {
+ z-index: 0;
+ top: 50%;
+ left: 50%;
+ width: min(88%, 430px);
+ height: 210px;
+ border: 1px solid color-mix(in srgb, var(--vp-c-bg-elv) 64%, var(--reme-blue));
+ border-radius: 30px;
+ background:
+ linear-gradient(135deg, color-mix(in srgb, var(--vp-c-bg-elv) 92%, transparent), color-mix(in srgb, var(--vp-c-bg-soft) 76%, transparent)),
+ radial-gradient(circle at 15% 15%, rgba(25, 201, 176, 0.14), transparent 42%);
+ box-shadow:
+ 0 30px 70px rgba(19, 70, 91, 0.16),
+ inset 0 1px 0 color-mix(in srgb, white 72%, transparent);
+ backdrop-filter: blur(22px) saturate(135%);
+ transform: translate(-50%, -50%) rotate(-1.5deg);
+}
+
+.VPHero .image-container::after {
+ z-index: -1;
+ top: 50%;
+ left: 50%;
+ width: min(78%, 380px);
+ height: 210px;
+ border: 1px solid rgba(49, 86, 217, 0.17);
+ border-radius: 30px;
+ background: linear-gradient(135deg, rgba(25, 201, 176, 0.1), rgba(49, 86, 217, 0.11));
+ transform: translate(-46%, -48%) rotate(7deg);
+}
+
+.VPHero .image-src {
+ z-index: 1;
+ width: min(76%, 380px);
+ max-width: 380px !important;
+ max-height: 150px !important;
+ object-fit: contain;
+ filter: drop-shadow(0 12px 20px rgba(18, 78, 105, 0.16));
+}
+
+.VPHomeFeatures .item:nth-child(1) { --feature-accent: #18b99e; }
+.VPHomeFeatures .item:nth-child(2) { --feature-accent: #25a8dc; }
+.VPHomeFeatures .item:nth-child(3) { --feature-accent: #6575e8; }
+.VPHomeFeatures .item:nth-child(4) { --feature-accent: #e69a42; }
+
+.VPHomeFeatures .VPFeature {
+ border-color: color-mix(in srgb, var(--feature-accent) 28%, var(--vp-c-divider));
+ border-radius: 16px;
+ background:
+ radial-gradient(circle at 10% 4%, color-mix(in srgb, var(--feature-accent) 16%, transparent), transparent 52%),
+ linear-gradient(150deg, var(--vp-c-bg-elv), color-mix(in srgb, var(--feature-accent) 7%, var(--vp-c-bg-soft)));
+ box-shadow:
+ inset 0 1px 0 color-mix(in srgb, white 76%, transparent),
+ 0 8px 24px color-mix(in srgb, var(--feature-accent) 7%, transparent);
+ transition: transform 160ms ease, border-color 160ms ease, box-shadow 160ms ease;
+}
+
+.VPHomeFeatures .VPFeature .box {
+ display: grid;
+ grid-template-columns: auto minmax(0, 1fr);
+ grid-template-rows: auto 1fr;
+ column-gap: 12px;
+ align-items: center;
+}
+
+.VPHomeFeatures .VPFeature .icon {
+ grid-column: 1;
+ grid-row: 1;
+ width: auto;
+ height: auto;
+ margin: 0;
+ border: 0;
+ background: transparent;
+ box-shadow: none;
+ font-size: 25px;
+}
+
+.VPHomeFeatures .VPFeature .title {
+ grid-column: 2;
+ grid-row: 1;
+ color: color-mix(in srgb, var(--feature-accent) 22%, var(--vp-c-text-1));
+}
+
+.VPHomeFeatures .VPFeature .details {
+ grid-column: 1 / -1;
+ grid-row: 2;
+ align-self: start;
+ padding-top: 18px;
+}
+
+.VPHomeFeatures .VPFeature .link-text {
+ grid-column: 1 / -1;
+}
+
+.VPHomeFeatures .VPFeature:hover {
+ transform: translateY(-3px);
+ border-color: color-mix(in srgb, var(--feature-accent) 48%, var(--vp-c-divider));
+ box-shadow: 0 18px 38px color-mix(in srgb, var(--feature-accent) 15%, transparent);
+}
+
+.dark .VPHomeFeatures .VPFeature {
+ background:
+ radial-gradient(circle at 10% 4%, color-mix(in srgb, var(--feature-accent) 18%, transparent), transparent 54%),
+ linear-gradient(150deg, var(--vp-c-bg-elv), color-mix(in srgb, var(--feature-accent) 8%, var(--vp-c-bg-soft)));
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
+}
+
+@media (max-width: 768px) {
+ .vp-doc h1 { font-size: 34px; }
+ .vp-doc h2 { margin-top: 44px; font-size: 24px; }
+ .copy-markdown-wrap { margin-top: -8px; }
+ .VPHero .image-container::before,
+ .VPHero .image-container::after { height: 176px; border-radius: 24px; }
+ .VPHero .image-src { width: 72%; max-height: 120px !important; }
+}
diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts
new file mode 100644
index 00000000..5edbc9e7
--- /dev/null
+++ b/docs/.vitepress/theme/index.ts
@@ -0,0 +1,15 @@
+import { h } from "vue";
+import DefaultTheme from "vitepress/theme";
+import CopyMarkdownButton from "./CopyMarkdownButton.vue";
+import SourceLink from "./SourceLink.vue";
+import "./custom.css";
+
+export default {
+ extends: DefaultTheme,
+ Layout() {
+ return h(DefaultTheme.Layout, null, {
+ "doc-before": () => h(CopyMarkdownButton),
+ "doc-after": () => h(SourceLink),
+ });
+ },
+};
diff --git a/docs/en/configuration.md b/docs/en/configuration.md
new file mode 100644
index 00000000..49e84bc1
--- /dev/null
+++ b/docs/en/configuration.md
@@ -0,0 +1,157 @@
+---
+title: Configuration
+description: ReMe configuration files, environment expansion, command-line overrides, and core components.
+---
+
+# Configuration
+
+ReMe uses YAML or JSON to describe its Service, Jobs, and Components. The built-in default is `reme/config/default.yaml`. Select another configuration at startup and apply command-line overrides when needed.
+
+## Precedence
+
+Configuration is merged in this order, with later values winning:
+
+1. `application_defaults` from enabled plugins.
+2. The selected file; `default` is used when none is specified.
+3. CLI dot-notation overrides.
+
+```bash
+reme start
+reme start config=demo
+reme start config=/absolute/path/to/app.yaml
+reme start service.port=8181 workspace_dir=/data/reme
+```
+
+`config` accepts a built-in name or a `.yaml`, `.yml`, or `.json` file. Overrides are deep-merged, so changing `service.port` preserves sibling service settings.
+
+## CLI values
+
+Arguments use `key=value`; leading `-` or `--` is accepted:
+
+```bash
+reme start --service.port=8181 --service.web_enabled=false
+```
+
+Values support null, booleans, numbers, JSON arrays and objects, quoted JSON strings, and plain strings. Numeric-looking values with leading zeroes, such as `007`, remain strings. Quote values such as `"true"` in JSON when they must remain strings.
+
+## Environment variables
+
+Configuration recursively expands:
+
+```yaml
+api_key: ${LLM_API_KEY}
+base_url: ${LLM_BASE_URL:-https://example.com/v1}
+```
+
+`${VAR}` fails when undefined; `${VAR:-default}` uses its fallback. ReMe also searches for `.env` from the command's working directory through at most five parents.
+
+Keep secrets in `.env` or the process environment, never in committed configuration.
+
+## Application fields
+
+| Field | Default | Purpose |
+|---|---|---|
+| `app_name` | `ReMe` | Display name |
+| `workspace_dir` | `.reme` | User-owned workspace root, normalized to an absolute path |
+| `metadata_dir` | `metadata` | Rebuildable indexes, graphs, and catalogs |
+| `session_dir` | `session` | Agent sessions; standard transcripts use `session/dialog` |
+| `mem_session_dir` | `mem_session` | Agent-wrapper sessions and configuration |
+| `resource_dir` | `resource` | External resources |
+| `daily_dir` | `daily` | Daily memory |
+| `digest_dir` | `digest` | Consolidated long-term memory |
+| `timezone` | `Asia/Shanghai` | IANA timezone used for dates and cron jobs |
+| `language` | empty | Default language for LLM interactions |
+| `plugins` | `[]` | Installed plugins enabled for this Application |
+| `service` | HTTP | Service configuration |
+| `jobs` | default Jobs | Job configurations by name |
+| `components` | defaults | Components grouped by type and name |
+
+`session_dir` must remain workspace-relative.
+
+## LLM
+
+The default LLM uses an OpenAI-compatible interface:
+
+```yaml
+components:
+ as_llm:
+ default:
+ backend: openai
+ model: qwen3.7-plus
+ context_size: 200000
+ credential:
+ api_key: ${LLM_API_KEY:-}
+ base_url: ${LLM_BASE_URL:-}
+```
+
+Built-in registrations include `openai`, `anthropic`, `dashscope`, `deepseek`, `gemini`, `moonshot`, `ollama`, and `xai`. Their detailed model fields follow the corresponding AgentScope wrappers.
+
+File operations, BM25 search, and wikilink traversal do not require an LLM. Evolution workflows such as `auto_memory`, `auto_resource`, and `auto_dream` do.
+
+## Embeddings
+
+Vector retrieval is disabled by default. Credentials alone do not enable it: configure `as_embedding`, `embedding_store`, and connect the store to `file_store`.
+
+```yaml
+components:
+ as_embedding:
+ default:
+ backend: openai
+ model: text-embedding-v4
+ dimensions: 1024
+ credential:
+ api_key: ${EMBEDDING_API_KEY}
+ base_url: ${EMBEDDING_BASE_URL:-https://dashscope.aliyuncs.com/compatible-mode/v1}
+ embedding_store:
+ default:
+ backend: local
+ as_embedding: default
+ file_store:
+ default:
+ backend: local
+ embedding_store: default
+ keyword_index: default
+ file_graph: default
+```
+
+Rebuild the embedding index after changing the model or dimensions.
+
+## Service and Jobs
+
+Minimal HTTP configuration:
+
+```yaml
+service:
+ backend: http
+ host: 127.0.0.1
+ port: 2333
+ web_enabled: true
+ mcp_enabled: true
+ mcp_path: /mcp
+```
+
+A Job declares a backend, parameter schema, and ordered Steps:
+
+```yaml
+jobs:
+ example:
+ backend: base
+ description: Example job
+ parameters:
+ type: object
+ properties:
+ text: { type: string }
+ required: [text]
+ steps:
+ - backend: example_step
+```
+
+Set `enable_serve: false` to keep a Job internal. Background and cron Jobs are never service-exposed.
+
+## Inspect the effective configuration
+
+```bash
+reme app_config
+```
+
+The result is the merged, validated configuration with secrets redacted. Use it when diagnosing plugin or override precedence. The authoritative contracts remain `reme/schema/application_config.py` and `reme/config/default.yaml`.
diff --git a/docs/en/contributing.md b/docs/en/contributing.md
index ad962169..99c9fac1 100644
--- a/docs/en/contributing.md
+++ b/docs/en/contributing.md
@@ -209,6 +209,13 @@ Documentation lives under:
docs/
```
+User guides should have matching `docs/zh/` and `docs/en/` versions and appear in the corresponding navigation in
+`docs/.vitepress/config.mts`. The ReMe Studio, TypeScript, plugin, and benchmark READMEs remain canonical in their own
+directories; `github-pages/scripts/generate-content.mjs` mirrors them during builds. Never edit `.generated/` or `dist/`.
+
+The Job API reference is generated from `reme/config/default.yaml`. Update that YAML and its tests when a default Job
+contract changes rather than editing generated pages.
+
Documentation should:
- Use clear titles that directly identify a capability or flow.
@@ -216,6 +223,15 @@ Documentation should:
- Use real repository paths such as `reme/config/default.yaml`, `reme/steps/`, and `tests/unit/`.
- Describe default behavior according to the current code, `pyproject.toml`, and default configuration.
+Validate the documentation site with:
+
+```bash
+cd github-pages
+npm ci
+npm test
+npm run build
+```
+
---
## Getting Help
diff --git a/docs/en/faq.md b/docs/en/faq.md
new file mode 100644
index 00000000..6aeb113a
--- /dev/null
+++ b/docs/en/faq.md
@@ -0,0 +1,71 @@
+---
+title: Frequently Asked Questions
+description: Quick answers for ReMe installation, services, models, retrieval, files, and plugins.
+---
+
+# Frequently Asked Questions
+
+## Do basic file operations require a model API key?
+
+No. `write`, `read`, `list`, `stat`, BM25 search, and wikilink traversal work without model credentials. `auto_memory`, `auto_resource`, and `auto_dream` require an LLM.
+
+## Why is search still BM25-only after setting an embedding key?
+
+Embeddings are disabled by default. Configure `as_embedding` and `embedding_store`, then connect `file_store.default.embedding_store` to that component. See [Configuration](./configuration.md#embeddings).
+
+## Why did `reme reindex` not discover a new file?
+
+`reindex` rebuilds indexes from current `file_chunks`; it does not scan the workspace. Check `index_update_loop`, the watched directory and extension, and `health_check`.
+
+## How do I use another workspace?
+
+```bash
+reme start workspace_dir=/absolute/path/to/memory
+```
+
+Ordinary CLI calls discover the running service, so they do not need the workspace argument again.
+
+## What if port 2333 is occupied?
+
+Do not stop an unknown listener. Select another port:
+
+```bash
+reme start service.port=8181
+```
+
+Then confirm it with `reme find_reme`.
+
+## Why is an installed plugin missing its Jobs?
+
+Installation only makes the distribution discoverable in the active Python environment. Enable it for the Application:
+
+```bash
+reme start plugins='["auto-fin"]'
+```
+
+Restart a running service after changing package or enablement state.
+
+## May I edit workspace Markdown directly?
+
+Yes. Files are the source of truth and watchers ingest changes. Keep frontmatter valid, use complete workspace-relative wikilinks, and avoid unconditional concurrent saves.
+
+## May I expose ReMe publicly?
+
+Not with the default configuration alone. Jobs can write and delete, HTTP CORS is permissive, and there is no general authentication layer. Use a controlled network or authenticated TLS reverse proxy and restrict `service.jobs`.
+
+## How should I back up and migrate memory?
+
+Stop writes and back up the complete workspace. `session/`, `resource/`, `daily/`, and `digest/` are the key sources; `metadata/` can be backed up or rebuilt. See [Diagnostics, Backup, and Recovery](./operations.md).
+
+## Why is Studio unavailable?
+
+The base `reme-ai` package has no frontend assets. Install `reme-ai[web]` or `reme-ai[core]`, or set `service.web_static_dir`. Missing Studio assets do not disable the Job API.
+
+## Which capabilities does the running service expose?
+
+```bash
+reme help
+reme app_config
+```
+
+Static documentation describes defaults; plugins and custom configuration may change the active service.
diff --git a/docs/en/index.md b/docs/en/index.md
new file mode 100644
index 00000000..c4048ca8
--- /dev/null
+++ b/docs/en/index.md
@@ -0,0 +1,36 @@
+---
+layout: home
+title: ReMe Documentation
+titleTemplate: false
+hero:
+ name: ReMe
+ text: Memory that works for agents
+ tagline: Files remain yours. ReMe turns conversations and resources into readable, editable, searchable, interconnected local files.
+ image:
+ src: /reme-logo.svg
+ alt: ReMe logo
+ actions:
+ - theme: brand
+ text: Quick Start
+ link: /en/quick_start
+ - theme: alt
+ text: Configuration
+ link: /en/configuration
+features:
+ - icon: 📁
+ title: Memory as files
+ details: Workspace files are the durable source of truth; indexes, graphs, and caches remain rebuildable.
+ link: /en/memory_as_file
+ - icon: 🧠
+ title: Memory workflows
+ details: Turn conversations and resources into daily notes, then consolidate them into connected long-term digests.
+ link: /en/auto_memory
+ - icon: 🔎
+ title: Search and graph
+ details: Combine keyword and optional vector retrieval with wikilink graph expansion.
+ link: /en/memory_search
+ - icon: 🔌
+ title: Agent integrations
+ details: Connect existing agents through the CLI, HTTP, MCP, and host adapters.
+ link: /en/integrations
+---
diff --git a/docs/en/integrations.md b/docs/en/integrations.md
new file mode 100644
index 00000000..1e9bddc0
--- /dev/null
+++ b/docs/en/integrations.md
@@ -0,0 +1,63 @@
+---
+title: Agent Integrations
+description: Connect ReMe to agents through the CLI, HTTP, MCP, Skills, and host adapters.
+---
+
+# Agent Integrations
+
+ReMe keeps memory in an independent service and a user-owned workspace. Multiple agents can call the same memory system without binding storage to one model or host.
+
+## Choose an interface
+
+| Scenario | Recommended interface |
+|---|---|
+| Local script or hook | ReMe CLI |
+| Application backend | HTTP Client |
+| Tool-protocol host | MCP |
+| TypeScript agent | `@agentscope-ai/reme` |
+| Claude Code | MCP + Skill + Stop Hook |
+| Hermes Agent | Memory provider adapter |
+| Codex or another coding agent | `reme_memory` Skill or MCP |
+
+## General memory loop
+
+1. Before answering, call `search` for relevant memory.
+2. Use `read` on high-value results and `traverse` when relationships matter.
+3. Retain workspace-relative source paths in the answer.
+4. At session end, pass source messages to `auto_memory`.
+5. Let background or scheduled workflows consolidate daily notes into digest memory.
+
+An empty search result must remain empty; do not present model inference as recalled history.
+
+## MCP
+
+The default HTTP service exposes streamable HTTP MCP at `http://127.0.0.1:2333/mcp`. Common tools include `search`, `read`, `traverse`, `list`, `auto_memory`, and `proactive`.
+
+Use `service.jobs` to expose a read-only subset or keep write tools in a separate configuration.
+
+## CLI and Skill
+
+`skills/reme_memory/SKILL.md` defines a general workflow for agents that can run local commands: installation checks, service discovery, retrieval, reading, and persistence boundaries.
+
+It deliberately avoids silently modifying Python environments, stopping unknown processes on port conflicts, writing recalled tool output back as conversation source, or persisting credentials.
+
+## TypeScript, OpenClaw, and DeepSeek Harness
+
+The [`@agentscope-ai/reme` TypeScript package](./integrations/typescript.md) provides the shared HTTP client and host adapters. See the dedicated guides for [DeepSeek Harness](./integrations/dsh.md) and [OpenClaw](./integrations/openclaw.md).
+
+## Claude Code
+
+`integrations/claude_code/` provides streamable HTTP MCP configuration, a `reme-memory` Skill, and a Stop hook that calls `auto_memory_cc`. Follow that directory's README for installation.
+
+## Hermes Agent
+
+`integrations/hermes_agent/` provides a memory provider that recalls context before model calls and asynchronously invokes `auto_memory` after each turn.
+
+## Production guidance
+
+- choose a stable absolute `workspace_dir`;
+- reuse a service discovered by `reme find_reme`;
+- treat `reme help` as the active Job contract;
+- apply timeouts and failure logging to writes;
+- do not block the host's core response path when memory is temporarily unavailable;
+- use authentication, TLS, and a minimal Job allowlist for remote access.
diff --git a/docs/en/operations.md b/docs/en/operations.md
new file mode 100644
index 00000000..50160661
--- /dev/null
+++ b/docs/en/operations.md
@@ -0,0 +1,102 @@
+---
+title: Diagnostics, Backup, and Recovery
+description: ReMe health checks, logs, index maintenance, workspace backup, migration, and recovery.
+---
+
+# Diagnostics, Backup, and Recovery
+
+ReMe recovery protects user-owned workspace files and rebuilds catalogs, indexes, and graphs from those sources. Never delete or rewrite user memory merely to repair derived state.
+
+## Quick diagnosis
+
+Run these in order:
+
+```bash
+reme find_reme
+reme version
+reme health_check
+reme status
+reme app_config
+```
+
+- `find_reme` confirms the actual host, port, and PID;
+- `version` verifies that the CLI reaches the service;
+- `health_check` reports component health;
+- `status` estimates stateful component memory and process RSS;
+- `app_config` returns the effective configuration with secrets redacted.
+
+## Logs and common symptoms
+
+`log_to_console` and `log_to_file` control logging. For startup failures, inspect the first exception rather than later client connection errors.
+
+| Symptom | Check first |
+|---|---|
+| CLI cannot find ReMe | `reme find_reme`, process state, startup directory, and port |
+| Automatic memory fails | LLM backend, model, API key, and base URL |
+| Search is BM25-only | Whether an embedding store is connected to `file_store` |
+| New files are absent | Directory, extension, watcher, and `health_check` |
+| Studio fails but API works | Installed web extra, static path, and browser console |
+| Installed plugin is unavailable | Python interpreter, `plugins` configuration, and service restart |
+
+## Index maintenance
+
+```bash
+reme reindex scope=all
+reme reindex scope=bm25
+reme reindex scope=embedding
+```
+
+`reindex` rebuilds BM25 and/or embedding indexes from the current `file_chunks`. It does not scan the workspace, rechunk files, or rebuild the wikilink graph. Diagnose the watcher first when ingestion is the problem.
+
+Rebuild a daily index page separately:
+
+```bash
+reme daily_reindex date=2026-09-04
+```
+
+## Backup
+
+Stop writes or stop the service, then back up the complete workspace. The most important sources are:
+
+- `session/` for conversation sources;
+- `resource/` for external resources;
+- `daily/` for daily memory;
+- `digest/` for consolidated memory.
+
+`metadata/` contains indexes, graphs, and catalogs. Backing it up accelerates restoration, but it is not the sole source of truth.
+
+Use an explicit, stable absolute `workspace_dir` for durable deployments rather than relying on an incidental `.reme/` under the current directory.
+
+## Migrate a workspace
+
+1. Stop the old service to prevent writes during the copy.
+2. Copy the complete workspace while preserving timestamps.
+3. Start with the new absolute path:
+
+```bash
+reme start workspace_dir=/new/location/reme-memory
+```
+
+4. Run `health_check`, `status`, and a representative `search`.
+5. Rebuild embeddings if their model or dimensions changed.
+
+Do not push a workspace containing private conversations to a public repository.
+
+## Recover derived state
+
+Do not remove anything until a backup exists. Then:
+
+1. preserve `session/`, `resource/`, `daily/`, and `digest/`;
+2. record the effective configuration and component backends;
+3. verify that the failure is limited to `metadata/`;
+4. move suspect derived state to an isolated backup location;
+5. restart with the same configuration and let watchers rebuild;
+6. validate search, graph traversal, and daily indexes.
+
+The internal layout of metadata files is not a public automation contract.
+
+## Concurrent editing
+
+When Studio or an editor saves a complete file, pass the mtime from `stat` as `save.expected_mtime`. A save then fails if another actor changed the file after it was opened, avoiding silent overwrites.
+
+File Jobs enforce workspace containment and per-path locking. Do not bypass them to write arbitrary absolute paths.
diff --git a/docs/en/plugin_development.md b/docs/en/plugin_development.md
new file mode 100644
index 00000000..074033c3
--- /dev/null
+++ b/docs/en/plugin_development.md
@@ -0,0 +1,101 @@
+---
+title: Plugin Development
+description: Create, register, configure, test, and publish a ReMe plugin.
+---
+
+# Plugin Development
+
+A ReMe plugin is a regular Python distribution exposed through the `reme.plugins` entry-point group. Its package-level `plugin.yaml` can register Step and Component backends and provide default Application configuration.
+
+## Minimal structure
+
+```text
+my-plugin/
+├── pyproject.toml
+└── src/my_plugin/
+ ├── __init__.py
+ ├── plugin.yaml
+ └── steps.py
+```
+
+`pyproject.toml`:
+
+```toml
+[project.entry-points."reme.plugins"]
+my-plugin = "my_plugin"
+```
+
+`plugin.yaml`:
+
+```yaml
+name: my-plugin
+backends:
+ my_step: my_plugin.steps:MyStep
+application_defaults:
+ jobs:
+ my_action:
+ backend: base
+ description: Run my plugin action
+ parameters:
+ type: object
+ properties:
+ text: { type: string }
+ required: [text]
+ steps:
+ - backend: my_step
+```
+
+## Implement a Step
+
+```python
+from reme.components.component_registry import R
+from reme.steps.base_step import BaseStep
+
+
+@R.register("my_step")
+class MyStep(BaseStep):
+ async def execute(self):
+ self.context.response.answer = self.context.data["text"]
+```
+
+Step instances belong to one Job invocation. Put shared in-memory state under a narrow `app_context.metadata` key. Promote state that needs lifecycle, locking, or persistence to a Component or workspace file.
+
+## Configuration merge
+
+`application_defaults` is a partial `ApplicationConfig`:
+
+```text
+plugin defaults < selected/default config < CLI overrides
+```
+
+Plugins must not rewrite user configuration. Their backends enter an Application-local registry only when the plugin appears in that Application's `plugins` list.
+
+## Local validation
+
+```bash
+reme plugins validate ./path/to/my-plugin
+reme plugins install ./path/to/my-plugin --editable
+reme plugins list
+reme plugins show my-plugin
+reme start plugins='["my-plugin"]'
+reme my_action text=hello
+```
+
+Validation imports plugin code, so run it only for trusted sources.
+
+## Test boundaries
+
+- create workspaces with `tmp_path`;
+- mock network, model, and subprocess boundaries;
+- verify disabled plugins do not mutate the built-in registry;
+- verify plugin defaults and explicit configuration precedence;
+- keep tasks, clients, and executors under Component lifecycle;
+- never delete or rewrite user source files to repair derived state.
+
+The repository's Daily Paper, Auto Fin, LME, and BEAM plugins are complete examples.
+
+## Compatibility
+
+Legacy Python Plugin descriptors and the `reme.configs` entry point remain supported during migration, but new plugins should use `plugin.yaml`. Enablement always belongs to an Application rather than a process-global switch.
+
+See [Plugin Management](./plugin_management.md) for installation, upgrades, and removal.
diff --git a/docs/en/plugin_management.md b/docs/en/plugin_management.md
index 31b8400a..a2429787 100644
--- a/docs/en/plugin_management.md
+++ b/docs/en/plugin_management.md
@@ -176,6 +176,16 @@ When the application uses an MCP service, service-enabled plugin Jobs appear as
Custom application configs must provide the plugin's runtime dependencies, including an `agent_wrapper.default` and
the `search` and `read` Jobs used by Auto Fin.
+## Benchmark application presets
+
+The [LME](../../plugins/lme/README.md) and [BEAM](../../plugins/beam/README.md) plugins
+register their backends and plugin-owned Jobs in `plugin.yaml`. ReMe's built-in `benchmark`
+preset provides the shared core Jobs and components without inheriting `default`, so default
+background and cron jobs are not included. Install the selected benchmark plugin, then use
+`config=benchmark` together with `plugins=["lme"]` or `plugins=["beam"]`. The repository's
+benchmark runners enable the corresponding installed plugin automatically; editable installation
+keeps local plugin changes visible. Dataset runners remain under `benchmark/`.
+
## Uninstall a plugin
Use the plugin entry-point name, not necessarily the distribution name:
diff --git a/docs/en/quick_start.md b/docs/en/quick_start.md
index 01b7714d..2b317245 100644
--- a/docs/en/quick_start.md
+++ b/docs/en/quick_start.md
@@ -1,5 +1,12 @@
+---
+title: Quick Start
+description: Install and start ReMe, then complete a first file, retrieval, and automatic-memory workflow.
+---
+
# Quick Start
+This page gets one working loop running. See [Configuration](./configuration.md) for the full configuration contract and [Services and Deployment](./services.md) for HTTP or MCP integration.
+
## Installation
ReMe requires Python 3.11+.
@@ -237,3 +244,5 @@ You can also specify a YAML or JSON configuration file:
```bash
reme start config=/path/to/custom.yaml
```
+
+Continue with the [CLI Reference](./reference/cli.md), [Job API Reference](./reference/jobs.md), or [Diagnostics, Backup, and Recovery](./operations.md).
diff --git a/docs/en/reference/cli.md b/docs/en/reference/cli.md
new file mode 100644
index 00000000..5a3c372d
--- /dev/null
+++ b/docs/en/reference/cli.md
@@ -0,0 +1,87 @@
+---
+title: CLI Reference
+description: ReMe command syntax, service invocation, configuration overrides, and plugin commands.
+---
+
+# CLI Reference
+
+The basic syntax is:
+
+```text
+reme ACTION key=value ...
+```
+
+## Start an Application
+
+```bash
+reme start
+reme start config=demo
+reme start workspace_dir=/data/reme service.port=8181
+reme start job=search query="keywords" limit=5
+```
+
+`start job=` runs one Job through a one-shot service; plain `start` runs the configured Service.
+
+## Call Jobs
+
+Once a service is running, each action name is a Job name:
+
+```bash
+reme help
+reme health_check
+reme search query="project decision" limit=10
+reme read path=digest/wiki/project.md start_line=1 end_line=80
+```
+
+Use JSON for structured values:
+
+```bash
+reme auto_memory \
+ session_id=example \
+ messages='[{"role":"user","content":"Remember this preference"}]'
+```
+
+Client-selection arguments—`backend`, `transport`, `host`, `port`, `timeout`, `command`, `args`, and `show_metadata`—configure the client and never leak into the Job payload.
+
+## Configuration overrides
+
+```bash
+reme start \
+ config=/path/to/custom.yaml \
+ service.port=8181 \
+ service.web_enabled=false \
+ plugins='["auto-fin"]'
+```
+
+Leading `-` or `--` is optional. Use dots for nested keys and JSON for arrays and objects.
+
+## Service discovery
+
+```bash
+reme find_reme
+```
+
+This reports a discovered service but never starts or replaces a process.
+
+## Plugin commands
+
+Package management runs locally rather than through HTTP or MCP:
+
+```bash
+reme plugins list
+reme plugins show auto-fin
+reme plugins validate auto-fin
+reme plugins install reme-auto-fin
+reme plugins uninstall auto-fin
+```
+
+See [Plugin Management](../plugin_management.md) for the complete workflow.
+
+## Discover active capabilities
+
+The [Job API Reference](./jobs.md) describes the default configuration. Plugins and custom YAML may change the running service, so automation should prefer:
+
+```bash
+reme help
+reme app_config
+```
diff --git a/docs/en/services.md b/docs/en/services.md
new file mode 100644
index 00000000..c0be01a0
--- /dev/null
+++ b/docs/en/services.md
@@ -0,0 +1,115 @@
+---
+title: Services and Deployment
+description: Run ReMe through HTTP, SSE, MCP, the CLI, and ReMe Studio while respecting its local security boundary.
+---
+
+# Services and Deployment
+
+ReMe can run as a local HTTP service, a standalone MCP server, or a one-shot CLI Job. The default starts HTTP on `127.0.0.1:2333` and serves JSON, SSE, streamable HTTP MCP, and optional ReMe Studio from one process.
+
+## HTTP API
+
+```bash
+reme start
+reme start service.host=127.0.0.1 service.port=8181
+```
+
+Regular Jobs become `POST /`:
+
+```bash
+curl -s http://127.0.0.1:2333/search \
+ -H 'Content-Type: application/json' \
+ -d '{"query":"user preferences","limit":5}'
+```
+
+Job arguments live at the request body's top level. A regular response follows the `Response` schema:
+
+```json
+{"success": true, "answer": "...", "metadata": {}}
+```
+
+Unhandled Step failures become unsuccessful responses.
+
+## Streaming and SSE
+
+Jobs with `backend: stream` also use `POST /`, returning `text/event-stream`. Error paths emit an error chunk and always terminate the stream. The default `chat` Job is streaming:
+
+```bash
+curl -N http://127.0.0.1:2333/chat \
+ -H 'Content-Type: application/json' \
+ -d '{"query":"Summarize my long-term preferences"}'
+```
+
+MCP does not expose Stream Jobs.
+
+## MCP
+
+The default HTTP service mounts streamable HTTP MCP at `/mcp`:
+
+```yaml
+service:
+ backend: http
+ mcp_enabled: true
+ mcp_path: /mcp
+ mcp_stateless_http: false
+```
+
+For a standalone MCP service:
+
+```bash
+reme start service.backend=mcp service.transport=stdio
+reme start service.backend=mcp service.transport=sse service.port=2333
+reme start service.backend=mcp service.transport=streamable-http service.port=2333
+```
+
+MCP tools come from non-stream Jobs with `enable_serve: true`. Use `service.jobs` as an allowlist. `injected_job_kwargs` adds server-managed values that callers cannot override.
+
+```yaml
+service:
+ backend: http
+ jobs: [search, read, traverse, auto_memory]
+ injected_job_kwargs:
+ tenant_id: local-user
+ tool_error_on_failure: true
+```
+
+## ReMe Studio
+
+After installing `reme-ai[web]` or `reme-ai[core]`, the default HTTP origin also serves Studio:
+
+```text
+http://127.0.0.1:2333/
+```
+
+Disable it with `service.web_enabled=false` or select a build with `service.web_static_dir`. Missing static assets do not prevent the Job API from starting.
+
+## One-shot Jobs
+
+```bash
+reme start job=search query="user preferences" limit=5
+```
+
+This selects the one-shot CLI Service while retaining the normal Application, Component, and Job lifecycle.
+
+## Service discovery
+
+```bash
+reme find_reme
+```
+
+Ordinary `reme ` commands prefer the running service's actual backend, host, port, and transport. They fall back to local configuration only when no service is discovered.
+
+## Security boundary
+
+ReMe is local-first:
+
+- the default binds to `127.0.0.1`;
+- HTTP CORS allows any origin;
+- Jobs may write, move, or delete files;
+- the service layer has no general-purpose user authentication.
+
+Do not expose the default service directly to the public internet. For remote access, place it on a controlled network or behind an authenticated TLS reverse proxy, apply access controls and request-size limits, and expose only necessary Jobs.
+
+## OpenAPI
+
+FastAPI exposes the active endpoints through `/docs`, `/redoc`, and `/openapi.json`. The Studio SPA fallback preserves these reserved paths.
diff --git a/docs/figure/reme-icon.svg b/docs/figure/reme-icon.svg
new file mode 100644
index 00000000..bbb9e6c8
--- /dev/null
+++ b/docs/figure/reme-icon.svg
@@ -0,0 +1,27 @@
+
diff --git a/docs/figure/reme-logo-fashion.svg b/docs/figure/reme-logo-fashion.svg
new file mode 100644
index 00000000..6fe1c868
--- /dev/null
+++ b/docs/figure/reme-logo-fashion.svg
@@ -0,0 +1,46 @@
+
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 00000000..a34cf9c2
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,43 @@
+---
+layout: home
+title: ReMe Documentation
+titleTemplate: false
+head:
+ - - meta
+ - http-equiv: refresh
+ content: "0; url=/zh/"
+ - - link
+ - rel: canonical
+ href: https://reme.agentscope.io/zh/
+hero:
+ name: ReMe
+ text: 让 Agent 真正记住
+ tagline: 文件属于你,记忆服务于 Agent。ReMe 将对话和资料沉淀为可读、可编辑、可检索、相互链接的本地文件。
+ image:
+ src: /reme-logo.svg
+ alt: ReMe Logo
+ actions:
+ - theme: brand
+ text: 快速开始
+ link: /zh/quick_start
+ - theme: alt
+ text: 核心概念
+ link: /zh/memory_as_file
+features:
+ - icon: 📁
+ title: Local-first
+ details: Workspace 文件是持久事实源;索引、图谱和缓存都可以重新构建。
+ link: /zh/memory_as_file
+ - icon: 🧠
+ title: Memory workflows
+ details: 将对话和资料写入 daily,自动整理为互相关联的长期 digest。
+ link: /zh/auto_memory
+ - icon: 🔎
+ title: Search and graph
+ details: 结合关键词、可选向量检索与 wikilink 图谱,渐进式展开上下文。
+ link: /zh/memory_search
+ - icon: 🔌
+ title: Agent integrations
+ details: 通过 CLI、HTTP、MCP 和宿主适配器接入已有 Agent。
+ link: /zh/integrations
+---
diff --git a/docs/zh/configuration.md b/docs/zh/configuration.md
new file mode 100644
index 00000000..6fdeb191
--- /dev/null
+++ b/docs/zh/configuration.md
@@ -0,0 +1,168 @@
+---
+title: 基础配置
+description: ReMe 配置文件、环境变量、命令行覆盖和核心组件配置。
+---
+
+# 基础配置
+
+ReMe 使用 YAML 或 JSON 描述 Service、Job 和 Component。默认配置位于 `reme/config/default.yaml`;启动时可以选择其他配置,再用命令行覆盖其中的字段。
+
+## 配置优先级
+
+配置按下面的顺序合并,靠后的值优先:
+
+1. 已启用插件提供的 `application_defaults`。
+2. 选中的配置文件;未指定时使用内置 `default`。
+3. 命令行 dot notation 覆盖。
+
+```bash
+reme start
+reme start config=demo
+reme start config=/absolute/path/to/app.yaml
+reme start service.port=8181 workspace_dir=/data/reme
+```
+
+`config` 支持内置配置名以及 `.yaml`、`.yml`、`.json` 文件。覆盖采用深度合并,不会因为修改 `service.port` 而丢失 `service` 下的其他字段。
+
+## 值的解析
+
+CLI 参数使用 `key=value`,前导 `-` 或 `--` 也会被接受:
+
+```bash
+reme start --service.port=8181 --service.web_enabled=false
+```
+
+值支持:
+
+- `null`、布尔值、整数和浮点数;
+- JSON 数组和对象;
+- JSON 引号字符串;
+- 普通字符串。
+
+类似 `007` 的前导零字符串不会被转换成数字。需要保留 `true`、`false` 等字面字符串时,使用 JSON 引号:`value='"true"'`。
+
+## 环境变量
+
+配置文件会递归展开两种表达式:
+
+```yaml
+api_key: ${LLM_API_KEY}
+base_url: ${LLM_BASE_URL:-https://example.com/v1}
+```
+
+`${VAR}` 在变量未定义时会报错;`${VAR:-default}` 使用默认值。ReMe 还会从命令启动目录向上查找 `.env`,最多检查五级父目录。
+
+不要把密钥提交到配置文件或 Git。推荐把密钥放在 `.env` 或进程环境中。
+
+## Application 字段
+
+| 字段 | 默认值 | 作用 |
+|---|---|---|
+| `app_name` | `ReMe` | 应用显示名称 |
+| `workspace_dir` | `.reme` | 用户拥有的 workspace 根目录,会规范化为绝对路径 |
+| `metadata_dir` | `metadata` | 索引、图谱和 catalog 等派生状态 |
+| `session_dir` | `session` | Agent 对话记录;标准 transcript 位于 `session/dialog` |
+| `mem_session_dir` | `mem_session` | Agent wrapper 的会话和配置 |
+| `resource_dir` | `resource` | 外部资料 |
+| `daily_dir` | `daily` | Daily memory |
+| `digest_dir` | `digest` | 长期整理后的记忆 |
+| `timezone` | `Asia/Shanghai` | Cron、日期和梦境流程使用的 IANA 时区 |
+| `language` | 空 | LLM 交互默认语言 |
+| `plugins` | `[]` | 为当前 Application 启用的已安装插件 |
+| `service` | HTTP | 服务端配置 |
+| `jobs` | 默认 Job | Job 名到 Job 配置的映射 |
+| `components` | 默认组件 | 按类型和名称组织的组件配置 |
+
+`session_dir` 必须保持 workspace-relative。其他 workspace 子目录也应使用清晰、稳定的相对名称。
+
+## LLM 配置
+
+默认 LLM 使用 OpenAI-compatible 接口:
+
+```yaml
+components:
+ as_llm:
+ default:
+ backend: openai
+ model: qwen3.7-plus
+ context_size: 200000
+ credential:
+ api_key: ${LLM_API_KEY:-}
+ base_url: ${LLM_BASE_URL:-}
+```
+
+可注册的内置 backend 包括 `openai`、`anthropic`、`dashscope`、`deepseek`、`gemini`、`moonshot`、`ollama` 和 `xai`。实际字段由对应 AgentScope model wrapper 决定。
+
+基础文件操作、BM25 检索、wikilink 遍历不需要 LLM。`auto_memory`、`auto_resource`、`auto_dream` 等演化流程需要可用 LLM。
+
+## Embedding 配置
+
+向量检索默认关闭。只设置 `EMBEDDING_API_KEY` 不会自动启用它;还需要同时启用 `as_embedding`、`embedding_store`,并把它连接到 `file_store`:
+
+```yaml
+components:
+ as_embedding:
+ default:
+ backend: openai
+ model: text-embedding-v4
+ dimensions: 1024
+ credential:
+ api_key: ${EMBEDDING_API_KEY}
+ base_url: ${EMBEDDING_BASE_URL:-https://dashscope.aliyuncs.com/compatible-mode/v1}
+ embedding_store:
+ default:
+ backend: local
+ as_embedding: default
+ file_store:
+ default:
+ backend: local
+ embedding_store: default
+ keyword_index: default
+ file_graph: default
+```
+
+修改 embedding 模型或维度后,应重新构建 embedding 索引。
+
+## Service 和 Job
+
+最小 HTTP 配置:
+
+```yaml
+service:
+ backend: http
+ host: 127.0.0.1
+ port: 2333
+ web_enabled: true
+ mcp_enabled: true
+ mcp_path: /mcp
+```
+
+Job 由 backend、参数 schema 和顺序执行的 Step 组成:
+
+```yaml
+jobs:
+ example:
+ backend: base
+ description: Example job
+ parameters:
+ type: object
+ properties:
+ text: { type: string }
+ required: [text]
+ steps:
+ - backend: example_step
+```
+
+设置 `enable_serve: false` 可以保留内部 Job、禁止 Service 暴露。后台和 Cron Job始终不会作为请求端点暴露。
+
+## 查看生效配置
+
+服务启动后运行:
+
+```bash
+reme app_config
+```
+
+返回的是已合并、已校验并隐藏密钥后的配置。排查覆盖顺序或插件配置时,应以它为准,而不是只查看某一个 YAML 文件。
+
+完整字段定义以 `reme/schema/application_config.py` 和 `reme/config/default.yaml` 为准。
diff --git a/docs/zh/contributing.md b/docs/zh/contributing.md
index 2b5c65e0..e2cdbdfc 100644
--- a/docs/zh/contributing.md
+++ b/docs/zh/contributing.md
@@ -190,6 +190,12 @@ pytest tests/unit/test_reme_cli.py
docs/
```
+用户指南应同时提供 `docs/zh/` 与 `docs/en/` 版本,并在 `docs/.vitepress/config.mts` 的对应导航中注册。ReMe Studio、
+TypeScript、插件和评测的 README 是各自目录中的规范源文件;`github-pages/scripts/generate-content.mjs` 会在构建时镜像它们,
+不要编辑 `.generated/` 或 `dist/`。
+
+`Job API 参考`由 `reme/config/default.yaml` 自动生成。修改默认 Job 参数时更新 YAML 和测试,不要手工维护生成页。
+
建议文档保持:
- 标题明确,直接说明能力或流程。
@@ -197,6 +203,15 @@ docs/
- 涉及路径时使用仓库内真实路径,例如 `reme/config/default.yaml`、`reme/steps/`、`tests/unit/`。
- 涉及默认行为时,以当前代码和 `pyproject.toml`、默认配置为准。
+文档站检查:
+
+```bash
+cd github-pages
+npm ci
+npm test
+npm run build
+```
+
---
## 获取帮助
diff --git a/docs/zh/faq.md b/docs/zh/faq.md
new file mode 100644
index 00000000..563a93bc
--- /dev/null
+++ b/docs/zh/faq.md
@@ -0,0 +1,75 @@
+---
+title: 常见问题
+description: ReMe 安装、服务、模型、检索、文件和插件问题的快速答案。
+---
+
+# 常见问题
+
+## 基础文件操作需要模型 API Key 吗?
+
+不需要。`write`、`read`、`list`、`stat`、BM25 搜索和 wikilink 遍历可以在没有模型凭据时运行。`auto_memory`、`auto_resource` 和 `auto_dream` 需要 LLM。
+
+## 为什么配置了 Embedding Key 仍然只有 BM25?
+
+Embedding 默认未启用。除了凭据,还必须配置 `as_embedding`、`embedding_store`,并让 `file_store.default.embedding_store` 指向该组件。参见[基础配置](./configuration.md#embedding-配置)。
+
+## 为什么 `reme reindex` 没有发现新文件?
+
+`reindex` 只从当前 `file_chunks` 重建 BM25 或 embedding 索引,不扫描 workspace。检查 `index_update_loop` watcher、文件目录、后缀和 `health_check`。
+
+## 怎样使用另一个 workspace?
+
+启动时传入稳定的绝对路径:
+
+```bash
+reme start workspace_dir=/absolute/path/to/memory
+```
+
+普通 CLI 调用会发现运行服务,不必重复 workspace 参数。
+
+## 端口 2333 被占用怎么办?
+
+不要停止未知监听者。选择另一个端口:
+
+```bash
+reme start service.port=8181
+```
+
+然后用 `reme find_reme` 确认发现结果。
+
+## 为什么插件安装后仍然没有对应 Job?
+
+安装只让 distribution 在当前 Python 环境中可见。还需要在应用配置中启用:
+
+```bash
+reme start plugins='["auto-fin"]'
+```
+
+修改安装或启用状态后需要重启已运行的服务。
+
+## 可以直接编辑 workspace 中的 Markdown 吗?
+
+可以。文件是事实源,watcher 会摄取修改。应保留有效 frontmatter、使用完整 workspace-relative wikilink,并避免同时由多个编辑器无条件覆盖同一文件。
+
+## 可以把服务暴露到公网吗?
+
+默认配置不适合直接公网暴露。服务包含写入和删除 Job,HTTP CORS 宽松,且没有通用认证层。请使用受控网络或带 TLS、认证、访问控制的反向代理,并限制 `service.jobs`。
+
+## 怎样备份和迁移?
+
+停止写入后备份整个 workspace。`session/`、`resource/`、`daily/` 和 `digest/` 是最重要的用户数据;`metadata/` 可以随同备份,也可以从源文件重建。详见[诊断、备份与恢复](./operations.md)。
+
+## Studio 找不到怎么办?
+
+基础 `reme-ai` 包不包含前端资源。安装 `reme-ai[web]` 或 `reme-ai[core]`,或通过 `service.web_static_dir` 指向构建产物。Studio 缺失不会影响 Job API。
+
+## 当前服务到底开放了哪些能力?
+
+运行:
+
+```bash
+reme help
+reme app_config
+```
+
+静态文档描述默认配置;运行服务可能由自定义配置和插件改变。
diff --git a/docs/zh/index.md b/docs/zh/index.md
new file mode 100644
index 00000000..b8e3436b
--- /dev/null
+++ b/docs/zh/index.md
@@ -0,0 +1,36 @@
+---
+layout: home
+title: ReMe 文档
+titleTemplate: false
+hero:
+ name: ReMe
+ text: 让 Agent 真正记住
+ tagline: 文件属于你,记忆服务于 Agent。ReMe 将对话和资料沉淀为可读、可编辑、可检索、相互链接的本地文件。
+ image:
+ src: /reme-logo.svg
+ alt: ReMe Logo
+ actions:
+ - theme: brand
+ text: 快速开始
+ link: /zh/quick_start
+ - theme: alt
+ text: 查看配置
+ link: /zh/configuration
+features:
+ - icon: 📁
+ title: 文件即记忆
+ details: Workspace 文件是持久事实源;索引、图谱和缓存都可以重新构建。
+ link: /zh/memory_as_file
+ - icon: 🧠
+ title: 自动记忆
+ details: 将对话和资料写入 daily,自动整理为互相关联的长期 digest。
+ link: /zh/auto_memory
+ - icon: 🔎
+ title: 检索与图谱
+ details: 结合关键词、可选向量检索与 wikilink 图谱,渐进式展开上下文。
+ link: /zh/memory_search
+ - icon: 🔌
+ title: Agent 集成
+ details: 通过 CLI、HTTP、MCP 和宿主适配器接入已有 Agent。
+ link: /zh/integrations
+---
diff --git a/docs/zh/integrations.md b/docs/zh/integrations.md
new file mode 100644
index 00000000..ce3dd841
--- /dev/null
+++ b/docs/zh/integrations.md
@@ -0,0 +1,88 @@
+---
+title: Agent 集成
+description: 通过 CLI、HTTP、MCP、Skill 和宿主适配器把 ReMe 接入 Agent。
+---
+
+# Agent 集成
+
+ReMe 把记忆能力放在独立服务和用户拥有的 workspace 中。Agent 可以通过标准接口调用同一套记忆,而不必把存储逻辑绑定到某一个模型或宿主。
+
+## 选择接入方式
+
+| 场景 | 推荐方式 |
+|---|---|
+| 本机脚本或 Hook | ReMe CLI |
+| 应用后端 | HTTP Client |
+| 支持工具协议的 Agent | MCP |
+| TypeScript Agent | `@agentscope-ai/reme` |
+| Claude Code | MCP + Skill + Stop Hook |
+| Hermes Agent | Memory provider adapter |
+| Codex 或其他 coding agent | `reme_memory` Skill 或 MCP |
+
+## 通用接入循环
+
+一个完整但可控的 Agent 记忆循环通常包含:
+
+1. 会话开始或回答前,用 `search` 找到相关记忆;
+2. 对高价值结果使用 `read`,必要时用 `traverse` 展开关系;
+3. 在回答中保留 workspace-relative 来源路径;
+4. 会话结束后,把原始消息交给 `auto_memory`;
+5. 由后台或定时任务把 daily 内容整理到 digest。
+
+搜索不到内容时应明确返回空结果,不应把模型推测当成历史记忆。
+
+## MCP
+
+默认 HTTP 服务在 `http://127.0.0.1:2333/mcp` 提供 streamable HTTP MCP。常用工具包括:
+
+- `search`
+- `read`
+- `traverse`
+- `list`
+- `auto_memory`
+- `proactive`
+
+根据宿主风险模型,可以用 `service.jobs` 只暴露只读工具,或将写入工具放在单独配置中。
+
+## CLI 和 Skill
+
+仓库中的 `skills/reme_memory/SKILL.md` 描述了一个通用 Agent 工作流,包括安装检查、服务发现、检索、读取和写入边界。它适合能够执行本地命令的 Agent。
+
+Skill 不应:
+
+- 未经允许安装或升级 Python 环境;
+- 发现端口冲突后停止未知进程;
+- 把召回的工具结果再次写入对话来源;
+- 将密钥或敏感信息写入记忆。
+
+## TypeScript、OpenClaw 与 DeepSeek Harness
+
+统一 HTTP 客户端和包能力见 [TypeScript Agent 集成](./integrations/typescript.md)。宿主的完整安装、配置和运行说明见 [DeepSeek Harness](./integrations/dsh.md) 与 [OpenClaw](./integrations/openclaw.md) 指南。它们包含:
+
+- HTTP Client;
+- DeepSeek Harness adapter;
+- OpenClaw adapter;
+- 构建与发布检查。
+
+## Claude Code
+
+仓库的 `integrations/claude_code/` 提供:
+
+- streamable HTTP MCP 配置;
+- `reme-memory` Skill;
+- 会话停止时调用 `auto_memory_cc` 的 Hook。
+
+完整安装步骤以仓库中的 `integrations/claude_code/README.md` 为准。
+
+## Hermes Agent
+
+`integrations/hermes_agent/` 提供 memory provider:模型调用前检索相关记忆,每轮结束后异步调用 `auto_memory`。完整配置见该目录 README。
+
+## 生产接入建议
+
+- 明确选择稳定、绝对的 `workspace_dir`;
+- 启动前复用 `reme find_reme` 发现的服务;
+- 以 `reme help` 为当前 Job 契约;
+- 为写入动作设置超时和失败日志;
+- 不因记忆服务暂时不可用而阻塞宿主的核心回答流程;
+- 对远程访问使用认证、TLS 和最小 Job allowlist。
diff --git a/docs/zh/integrations/claude-code.md b/docs/zh/integrations/claude-code.md
new file mode 100644
index 00000000..17f78ab4
--- /dev/null
+++ b/docs/zh/integrations/claude-code.md
@@ -0,0 +1,67 @@
+---
+title: Claude Code 集成
+description: 通过 MCP、reme-memory Skill 和 Stop Hook 将 Claude Code 连接到 ReMe。
+---
+
+# Claude Code 集成
+
+ReMe 的 Claude Code 插件提供长期记忆召回,并在每次会话结束后异步记录对话。Daily 到 digest 的整理仍由共享的 ReMe 服务负责。
+
+## 能力
+
+- 通过 MCP 使用 `search`、`traverse`、`daily_list`、`frontmatter_read`、`read`、`auto_memory_cc` 等工具;
+- `reme-memory` Skill 在回答前召回长期记忆并保留来源路径;
+- Stop Hook 只把 Claude Code `session_id` 传给服务端,服务端从本地 transcript 解析会话;
+- 记录在脱离 Claude Code 的后台进程中进行,不延迟退出;服务不可用时记录日志并结束。
+
+## 部署模型
+
+插件连接到用户预先启动的共享 HTTP MCP 服务,不为每个 Claude Code 窗口创建 ReMe。这样所有窗口共享一个 workspace、一组 watcher 和一次 dream cron。
+
+## 准备 ReMe
+
+```bash
+pip install "reme-ai[core]"
+```
+
+在稳定目录配置 LLM 环境,然后启动:
+
+```bash
+reme start service.backend=http
+```
+
+默认 JSON Job API 和 MCP 地址分别位于同一个 `127.0.0.1:2333` 服务,MCP 路径是 `/mcp`。使用其他端口时,必须同步修改插件的 `.mcp.json`。
+
+默认搜索使用 BM25;只有启用向量检索时才需要 Embedding 配置。
+
+## 安装插件
+
+在 Claude Code 中运行:
+
+```text
+/plugin marketplace add ./integrations/claude_code
+/plugin install reme@reme-marketplace
+```
+
+重启 Claude Code,再运行 `/mcp`,确认 `reme` server 和工具已经连接。
+
+## Hook 与路径
+
+- MCP 配置:`integrations/claude_code/reme/.mcp.json`;
+- 自动记忆 Hook:`integrations/claude_code/reme/hooks/auto_memory.py`;
+- Hook 日志:`integrations/claude_code/reme/logs/auto_memory_hook.log`;
+- 默认 transcript 根目录:`~/.claude/projects`;
+- 可通过 `CLAUDE_CONFIG_DIR` 修改 transcript 根目录;
+- 可通过 `REME_HOST`、`REME_PORT` 覆盖 Hook 使用的服务地址。
+
+Hook 需要 `python3` 位于 `PATH`。MCP 工具名前缀可能随 Claude Code 版本包含 server segment;Skill 使用 `mcp__reme__*` 匹配这一差异。
+
+## 验证
+
+1. `reme health_check` 返回健康;
+2. Claude Code `/mcp` 显示 ReMe;
+3. `reme-memory` 能召回一条已存在记忆;
+4. 完成测试会话后,Hook 日志没有错误;
+5. 对应内容出现在当天 daily note 中。
+
+英文原始部署说明位于 `integrations/claude_code/README.md`。
diff --git a/docs/zh/integrations/hermes.md b/docs/zh/integrations/hermes.md
new file mode 100644
index 00000000..2371b1f8
--- /dev/null
+++ b/docs/zh/integrations/hermes.md
@@ -0,0 +1,59 @@
+---
+title: Hermes Agent 集成
+description: 使用 ReMe memory provider 在 Hermes 调用模型前召回、每轮结束后异步记录。
+---
+
+# Hermes Agent 集成
+
+Hermes memory provider 连接到一个已经运行的 ReMe HTTP 服务,在每次模型调用前召回相关记忆,并在用户/助手回合完成后异步调用 `auto_memory`。
+
+## Workspace 隔离
+
+ReMe 的搜索范围是一个完整 workspace。多个 Hermes profile 指向同一个 workspace 时会共享召回结果;需要隔离时,为每个 profile 使用独立 workspace 和端点。
+
+```bash
+reme start \
+ workspace_dir="/absolute/path/to/reme-hermes-default" \
+ service.backend=http \
+ service.host=127.0.0.1 \
+ service.port=2333
+```
+
+自动记忆需要 LLM;默认 BM25 搜索不需要 Embedding。
+
+## 安装与配置
+
+```bash
+hermes plugins install agentscope-ai/ReMe/integrations/hermes_agent
+hermes memory setup
+```
+
+选择 `reme`,接受默认的 `http://127.0.0.1:2333`,或输入上一步使用的端点。Setup 会先调用 `health_check`,只有新端点健康时才替换现有 provider 配置。
+
+配置存放在 `$HERMES_HOME/reme.json`:
+
+```json
+{
+ "endpoint": "http://127.0.0.1:2333",
+ "request_timeout": 600.0,
+ "recall_timeout": 5.0,
+ "health_timeout": 2.0,
+ "health_retry_seconds": 30.0,
+ "shutdown_timeout": 30.0,
+ "recall_limit": 5
+}
+```
+
+运行 `hermes memory status` 检查安装和配置。新的 Hermes 会话还会重新检查端点健康状态。
+
+## 生命周期和失败行为
+
+- `prefetch` 调用 ReMe `search`,Hermes 将结果放入受保护的 memory context;
+- `sync_turn` 把完成的回合加入串行后台写队列,再调用 `auto_memory`;
+- cron、flush 和 subagent context 不写入对话记忆;
+- 健康检查失败后,在 cooldown 结束前暂停召回和记录;
+- 召回与写入有独立 cooldown,单项失败不会关闭另一项;
+- 召回使用较短超时,避免慢搜索长期阻塞模型调用;
+- shutdown 会在有限时间内排空写队列,ReMe 服务仍由用户独立管理。
+
+英文权威安装说明位于 `integrations/hermes_agent/README.md`。
diff --git a/docs/zh/operations.md b/docs/zh/operations.md
new file mode 100644
index 00000000..43e4bcbd
--- /dev/null
+++ b/docs/zh/operations.md
@@ -0,0 +1,106 @@
+---
+title: 诊断、备份与恢复
+description: ReMe 服务健康检查、日志、索引维护、备份迁移和常见恢复流程。
+---
+
+# 诊断、备份与恢复
+
+ReMe 的恢复原则是:保护 workspace 中的用户文件,通过源文件重建索引、图谱和 catalog。不要为了修复索引而删除或改写用户记忆。
+
+## 快速诊断
+
+依次执行:
+
+```bash
+reme find_reme
+reme version
+reme health_check
+reme status
+reme app_config
+```
+
+- `find_reme`:服务是否存在,以及实际 host、port、PID;
+- `version`:CLI 能否成功访问服务;
+- `health_check`:组件健康状态;
+- `status`:状态组件的内存估算和进程 RSS;
+- `app_config`:隐藏密钥后的实际生效配置。
+
+## 日志
+
+`log_to_console` 和 `log_to_file` 控制日志目标。排查启动失败时先查看第一条异常,而不是后续客户端连接错误。
+
+常见类别:
+
+| 现象 | 优先检查 |
+|---|---|
+| CLI 找不到服务 | `reme find_reme`、启动目录、端口和进程状态 |
+| 自动记忆失败 | LLM backend、model、API key、base URL |
+| 只有 BM25 结果 | embedding 组件是否真正接入 `file_store` |
+| 新文件没有进入搜索 | 文件所在目录、后缀、watcher 和 `health_check` |
+| Studio 空白但 API 正常 | web extra、静态构建路径和浏览器控制台 |
+| 插件安装后不可用 | 当前 Python 解释器、`plugins` 配置、服务重启 |
+
+## 索引维护
+
+```bash
+reme reindex scope=all
+reme reindex scope=bm25
+reme reindex scope=embedding
+```
+
+`reindex` 从当前 `file_chunks` 重建 BM25 和/或 embedding 派生索引。它不会扫描 workspace、重新分块或重建 wikilink 图谱。
+
+如果问题发生在文件摄取阶段,应先确认后台 watcher 正常运行;不能把 `reindex` 当成通用“重新扫描”命令。
+
+Daily 索引页可单独重建:
+
+```bash
+reme daily_reindex date=2026-09-04
+```
+
+## 备份
+
+停止写入或停止服务后,优先备份整个 workspace。最重要的目录是:
+
+- `session/`:原始对话来源;
+- `resource/`:外部资料;
+- `daily/`:每日记忆;
+- `digest/`:长期记忆。
+
+`metadata/` 包含索引、图谱和 file catalog。一起备份可以加速恢复,但它不是唯一事实源。
+
+不要只备份进程目录下偶然生成的 `.reme/`;生产使用应明确设置稳定的绝对 `workspace_dir`。
+
+## 迁移 workspace
+
+1. 停止旧服务,避免迁移期间继续写入。
+2. 复制完整 workspace,并保留文件时间信息。
+3. 使用新的绝对路径启动:
+
+```bash
+reme start workspace_dir=/new/location/reme-memory
+```
+
+4. 运行 `health_check`、`status` 和一次代表性 `search`。
+5. 如果 embedding 模型或维度发生变化,再重建 embedding 索引。
+
+普通 Markdown 和资源文件可以使用版本控制或同步工具;包含敏感对话的 workspace 不应推送到公开仓库。
+
+## 从派生状态故障恢复
+
+在确认备份可用前,不要删除任何内容。恢复顺序应为:
+
+1. 保留 `session/`、`resource/`、`daily/` 和 `digest/`;
+2. 记录当前配置和组件 backend;
+3. 确认故障只发生在 `metadata/`;
+4. 将有问题的派生状态移到隔离备份位置;
+5. 用同一配置启动 ReMe,让 watcher 从源文件重建;
+6. 验证搜索、图谱和 Daily 索引。
+
+具体 metadata 文件属于实现细节,不应在自动化脚本里依赖其内部格式。
+
+## 并发编辑
+
+Studio 或其他编辑器保存完整文件时,应使用 `stat` 返回的 mtime 作为 `save.expected_mtime`。如果文件在打开后被外部修改,保存会失败,从而避免静默覆盖。
+
+文件 Job 会校验 workspace containment,并对同一路径加锁。不要绕过这些 Job 直接向不受控制的绝对路径写入。
diff --git a/docs/zh/plugin_development.md b/docs/zh/plugin_development.md
new file mode 100644
index 00000000..67a6d10b
--- /dev/null
+++ b/docs/zh/plugin_development.md
@@ -0,0 +1,101 @@
+---
+title: 插件开发
+description: 创建、注册、配置、测试和发布 ReMe 插件。
+---
+
+# 插件开发
+
+ReMe 插件是一个普通 Python distribution,通过 `reme.plugins` entry-point group 暴露 package-level `plugin.yaml`。插件可以注册新的 Step、Component backend,并提供默认 Application 配置。
+
+## 最小结构
+
+```text
+my-plugin/
+├── pyproject.toml
+└── src/my_plugin/
+ ├── __init__.py
+ ├── plugin.yaml
+ └── steps.py
+```
+
+`pyproject.toml`:
+
+```toml
+[project.entry-points."reme.plugins"]
+my-plugin = "my_plugin"
+```
+
+`plugin.yaml`:
+
+```yaml
+name: my-plugin
+backends:
+ my_step: my_plugin.steps:MyStep
+application_defaults:
+ jobs:
+ my_action:
+ backend: base
+ description: Run my plugin action
+ parameters:
+ type: object
+ properties:
+ text: { type: string }
+ required: [text]
+ steps:
+ - backend: my_step
+```
+
+## 实现 Step
+
+```python
+from reme.components.component_registry import R
+from reme.steps.base_step import BaseStep
+
+
+@R.register("my_step")
+class MyStep(BaseStep):
+ async def execute(self):
+ self.context.response.answer = self.context.data["text"]
+```
+
+Step 实例属于单次 Job 调用。跨调用的内存状态应放在带命名空间的 `app_context.metadata`;需要生命周期、锁或持久化时,应升级为 Component 或 workspace 文件。
+
+## 配置合并
+
+`application_defaults` 是不完整的 `ApplicationConfig`。合并顺序为:
+
+```text
+插件默认值 < 选中/default 配置 < CLI 覆盖
+```
+
+插件不应自动修改用户的配置文件。只有在 Application 的 `plugins` 列表中显式启用后,插件 backend 才会加入该 Application 的局部 registry。
+
+## 本地验证
+
+```bash
+reme plugins validate ./path/to/my-plugin
+reme plugins install ./path/to/my-plugin --editable
+reme plugins list
+reme plugins show my-plugin
+reme start plugins='["my-plugin"]'
+reme my_action text=hello
+```
+
+校验会导入插件代码,因此只应对可信源码执行。
+
+## 测试边界
+
+- 使用 `tmp_path` 创建 workspace;
+- Mock 网络、模型和子进程;
+- 验证插件未启用时不会污染 built-in registry;
+- 验证默认配置与显式配置的合并优先级;
+- 验证后台任务、客户端和 executor 都由 Component 生命周期关闭;
+- 验证失败不会删除或重写用户源文件。
+
+仓库中的 `plugins/daily_paper`、`plugins/auto-fin`、`plugins/lme` 和 `plugins/beam` 是完整参考实现。
+
+## 兼容性
+
+迁移期间仍兼容旧的 Python Plugin descriptor 和 `reme.configs` entry point,但新插件应使用 `plugin.yaml`。不要依赖进程级全局 enable/disable 状态;插件启用始终属于具体 Application 配置。
+
+包管理命令、升级与卸载行为见[插件管理](./plugin_management.md)。
diff --git a/docs/zh/plugin_management.md b/docs/zh/plugin_management.md
index f9f735f8..a0cdfde7 100644
--- a/docs/zh/plugin_management.md
+++ b/docs/zh/plugin_management.md
@@ -169,6 +169,15 @@ curl -s http://127.0.0.1:2333/auto_fin \
自定义应用配置需要提供插件的运行依赖,包括 `agent_wrapper.default`,以及 Auto Fin 使用的 `search` 和 `read` Jobs。
+## Benchmark 应用配置
+
+[LME](../../plugins/lme/README_ZH.md) 和 [BEAM](../../plugins/beam/README_ZH.md) 插件通过
+`plugin.yaml` 注册 backend 和插件拥有的 Job。ReMe 内置的 `benchmark` 配置提供公共核心 Job 和
+Component,并且不继承 `default`,因此不包含默认后台和定时任务。先安装所需的 benchmark 插件,
+再使用 `config=benchmark`,同时指定 `plugins=["lme"]` 或 `plugins=["beam"]`。仓库内的 benchmark
+runner 会自动启用对应的已安装插件;editable 安装可让本地源码修改直接生效。数据集 runner 仍位于
+`benchmark/`。
+
## 卸载插件
这里使用插件 entry-point 名称,它不一定等于 distribution 名称:
diff --git a/docs/zh/quick_start.md b/docs/zh/quick_start.md
index f8f14340..3c57f626 100644
--- a/docs/zh/quick_start.md
+++ b/docs/zh/quick_start.md
@@ -1,5 +1,12 @@
+---
+title: 快速开始
+description: 安装并启动 ReMe,完成文件写入、检索和自动记忆的第一个闭环。
+---
+
# 快速开始
+本页用于完成第一次可运行闭环。需要完整配置字段时查看[基础配置](./configuration.md);接入 HTTP 或 MCP 时查看[服务与部署](./services.md)。
+
## 安装
ReMe 要求 Python 3.11+。
@@ -229,3 +236,5 @@ reme start \
```bash
reme start config=/path/to/custom.yaml
```
+
+下一步可以查看 [CLI 参考](./reference/cli.md)、[Job API 参考](./reference/jobs.md)和[诊断、备份与恢复](./operations.md)。
diff --git a/docs/zh/reference/cli.md b/docs/zh/reference/cli.md
new file mode 100644
index 00000000..3caa7cde
--- /dev/null
+++ b/docs/zh/reference/cli.md
@@ -0,0 +1,87 @@
+---
+title: CLI 参考
+description: ReMe 命令行语法、服务调用、配置覆盖与插件命令。
+---
+
+# CLI 参考
+
+ReMe 的基本语法是:
+
+```text
+reme ACTION key=value ...
+```
+
+## 启动应用
+
+```bash
+reme start
+reme start config=demo
+reme start workspace_dir=/data/reme service.port=8181
+reme start job=search query="关键词" limit=5
+```
+
+`start job=` 运行一次性 Job;普通 `start` 启动配置中的 Service。
+
+## 调用 Job
+
+服务运行后,Action 名就是 Job 名:
+
+```bash
+reme help
+reme health_check
+reme search query="项目决策" limit=10
+reme read path=digest/wiki/project.md start_line=1 end_line=80
+```
+
+复杂值使用 JSON:
+
+```bash
+reme auto_memory \
+ session_id=example \
+ messages='[{"role":"user","content":"记住这条偏好"}]'
+```
+
+服务选择参数 `backend`、`transport`、`host`、`port`、`timeout`、`command`、`args` 和 `show_metadata` 只用于构造客户端,不会泄漏到 Job 参数。
+
+## 配置覆盖
+
+```bash
+reme start \
+ config=/path/to/custom.yaml \
+ service.port=8181 \
+ service.web_enabled=false \
+ plugins='["auto-fin"]'
+```
+
+参数前的 `-` 或 `--` 可以省略。嵌套键使用点号,数组和对象使用 JSON。
+
+## 服务发现
+
+```bash
+reme find_reme
+```
+
+成功时输出可复用的服务信息;找不到服务时不会自动启动新进程。
+
+## 插件命令
+
+插件包管理是本地命令,不经过 HTTP 或 MCP:
+
+```bash
+reme plugins list
+reme plugins show auto-fin
+reme plugins validate auto-fin
+reme plugins install reme-auto-fin
+reme plugins uninstall auto-fin
+```
+
+完整说明见[插件管理](../plugin_management.md)。
+
+## 发现当前能力
+
+默认 Job 参数见 [Job API 参考](./jobs.md)。运行配置可能由插件和自定义 YAML 改变,因此自动化程序应优先调用:
+
+```bash
+reme help
+reme app_config
+```
diff --git a/docs/zh/services.md b/docs/zh/services.md
new file mode 100644
index 00000000..9cfea053
--- /dev/null
+++ b/docs/zh/services.md
@@ -0,0 +1,131 @@
+---
+title: 服务与部署
+description: 使用 ReMe 的 HTTP、SSE、MCP 和 Studio 服务,并理解默认安全边界。
+---
+
+# 服务与部署
+
+ReMe 可以作为本地 HTTP 服务、独立 MCP Server 或一次性 CLI Job 运行。默认模式是在 `127.0.0.1:2333` 启动 HTTP 服务,并在同一进程中提供 JSON API、SSE、MCP 与可选的 ReMe Studio。
+
+## HTTP 服务
+
+```bash
+reme start
+reme start service.host=127.0.0.1 service.port=8181
+```
+
+普通 Job 暴露为 `POST /`:
+
+```bash
+curl -s http://127.0.0.1:2333/search \
+ -H 'Content-Type: application/json' \
+ -d '{"query":"用户偏好","limit":5}'
+```
+
+请求体允许 Job 参数位于顶层。响应遵循 `Response`:
+
+```json
+{
+ "success": true,
+ "answer": "...",
+ "metadata": {}
+}
+```
+
+未被 Step 捕获的错误会转换成 `success: false` 的响应。
+
+## Streaming 与 SSE
+
+`backend: stream` 的 Job 仍使用 `POST /`,但响应类型是 `text/event-stream`。每个 chunk 使用统一的 streaming schema;失败时会发出错误 chunk,并以终止事件结束。
+
+默认 `chat` 是 Stream Job:
+
+```bash
+curl -N http://127.0.0.1:2333/chat \
+ -H 'Content-Type: application/json' \
+ -d '{"query":"总结我的长期偏好"}'
+```
+
+MCP 不暴露 Stream Job。
+
+## MCP
+
+默认 HTTP 服务会在 `/mcp` 挂载 streamable HTTP MCP:
+
+```yaml
+service:
+ backend: http
+ mcp_enabled: true
+ mcp_path: /mcp
+ mcp_stateless_http: false
+```
+
+也可以改用独立 MCP Service:
+
+```bash
+reme start service.backend=mcp service.transport=stdio
+reme start service.backend=mcp service.transport=sse service.port=2333
+reme start service.backend=mcp service.transport=streamable-http service.port=2333
+```
+
+MCP tool 来自 `enable_serve: true` 的非流式 Job。`service.jobs` 可以设置允许列表;`injected_job_kwargs` 可以注入服务端管理、调用方不能覆盖的参数。
+
+```yaml
+service:
+ backend: http
+ jobs: [search, read, traverse, auto_memory]
+ injected_job_kwargs:
+ tenant_id: local-user
+ tool_error_on_failure: true
+```
+
+## ReMe Studio
+
+安装 `reme-ai[web]` 或 `reme-ai[core]` 后,默认 HTTP 地址同时提供 ReMe Studio:
+
+```text
+http://127.0.0.1:2333/
+```
+
+可通过配置关闭或指定自定义构建:
+
+```yaml
+service:
+ web_enabled: false
+ # web_static_dir: /absolute/path/to/static
+```
+
+静态资源缺失不会阻止 Job API 启动。
+
+## 一次性 Job
+
+需要脚本式执行而不常驻服务时:
+
+```bash
+reme start job=search query="用户偏好" limit=5
+```
+
+它会切换到一次性 CLI Service,但仍经过正常的 Application、Component 和 Job 生命周期。
+
+## 服务发现
+
+```bash
+reme find_reme
+```
+
+ReMe 会记录本机运行服务的启动参数。普通 `reme ` 优先使用实际运行服务的 backend、host、port 和 transport;找不到运行服务时才回退到本地配置。
+
+## 安全边界
+
+ReMe 默认定位为本地服务:
+
+- 默认绑定 `127.0.0.1`;
+- HTTP CORS 配置允许任意 origin;
+- Job 可进行文件写入、移动和删除;
+- 当前服务层不提供通用用户认证。
+
+不要直接把默认服务暴露到公网。需要远程访问时,在受控网络或带身份认证、TLS、访问控制和请求大小限制的反向代理后部署,并通过 `service.jobs` 只开放必要 Job。
+
+## OpenAPI
+
+HTTP 服务使用 FastAPI。启用 HTTP 服务时,可以通过 `/docs`、`/redoc` 和 `/openapi.json` 查看当前配置实际注册的端点;Studio 的 SPA fallback 不会覆盖这些保留路径。
diff --git a/github-pages/README.md b/github-pages/README.md
index 2be3e6a4..c4b09810 100644
--- a/github-pages/README.md
+++ b/github-pages/README.md
@@ -1,8 +1,8 @@
-# ReMe GitHub Pages
+# ReMe documentation site
-This directory contains the standalone Vite documentation site published at . The
-GitHub Pages fallback is . It does not depend on the ReMe Studio application in
-`reme_studio/`.
+This package builds the VitePress site published at . The canonical documentation lives in
+`docs/`; selected product, integration, plugin, and benchmark READMEs are mirrored into a disposable source tree during
+the build. Do not edit `.generated/` or `dist/`.
## Requirements
@@ -11,79 +11,41 @@ GitHub Pages fallback is . It does not de
## Local development
-From the repository root:
-
```bash
cd github-pages
-npm install
+npm ci
npm run dev
```
-Open the URL printed by Vite, normally . The development server watches the frontend source.
-When a repository Markdown file changes, restart the development command to regenerate the documentation content.
+The development server prints its local URL. Restart it after changing a mirrored README or `reme/config/default.yaml`
+so the generated source tree and Job reference are refreshed. Changes under `docs/` are also refreshed on restart.
-For subsequent installs or CI-compatible dependency installation, use:
-
-```bash
-npm ci
-```
-
-## Preview the production build
-
-Build and start the preview server:
+## Validation
```bash
+npm test
npm run build
npm run preview
```
-Open the URL printed by Vite, normally . Production assets use relative paths so the same build
-works on both the custom domain and the GitHub Pages project path.
+The test suite verifies bilingual core pages, canonical-source mappings, generated Job coverage, and disposable output.
+The production build is written to `github-pages/dist/` for the existing GitHub Pages workflow.
-The generated `dist/` and `.generated/` directories are disposable build output and are excluded from Git.
+## Sources
-## Documentation sources
-
-The build script reads the canonical repository files directly. Do not edit generated copies under `.generated/` or
-`dist/`.
-
-- `README.md` and `README_ZH.md`: project introductions
-- `docs/en/` and `docs/zh/`: English and Chinese guides
-- `docs/figure/`: documentation images
-- `reme_studio/README.md` and `reme_studio/README_ZH.md`: ReMe Studio guide
-- `typescript/README.md` and `typescript/README_ZH.md`: TypeScript client, DeepSeek Harness, and OpenClaw integration guide
+- `docs/`: canonical guides, VitePress configuration, theme, and brand assets
+- `reme/config/default.yaml`: generated callable Job reference
+- `reme_studio/README*.md`: ReMe Studio
+- `typescript/README*.md`: TypeScript client and adapters
- `plugins/*/README*.md`: plugin guides
-- `benchmark/{beam,longmemeval,pibench,toolmemory}/README*.md`: benchmark guides and results
-- `AGENTS.md`: repository development guide
+- `benchmark/*/README*.md`: benchmark guides
+- `scripts/generate-content.mjs`: source mirroring and reference generation
-To add or reorganize a document in the site navigation, update
-[`scripts/generate-content.mjs`](./scripts/generate-content.mjs). Presentation and interaction code lives in `src/`.
-
-## Project structure
-
-```text
-github-pages/
-├── index.html
-├── package.json
-├── scripts/
-│ └── generate-content.mjs
-├── src/
-│ ├── main.js
-│ └── styles.css
-└── vite.config.js
-```
+When adding a canonical guide, add both `docs/zh/.md` and `docs/en/.md`, then include it in the appropriate
+sidebar in `docs/.vitepress/config.mts`. Add repository-owned READMEs to `externalDocuments` in the generator rather than
+duplicating their full content under `docs/`.
## Deployment
-The repository workflow `.github/workflows/deploy-docs.yml` builds this directory and publishes `dist/` to GitHub Pages.
-It runs after relevant documentation or site files change on `main`, and it can also be started manually from the
-GitHub Actions page.
-
-The repository's **Settings → Pages → Build and deployment → Source** must be set to **GitHub Actions**. Its custom
-domain must be set to `reme.agentscope.io`; `public/CNAME` preserves that domain in the published artifact.
-
-Useful links:
-
-- ReMe documentation:
-- GitHub Pages fallback:
-- ReMe repository:
+`.github/workflows/deploy-docs.yml` uses the reusable documentation build workflow and publishes `dist/` to GitHub
+Pages. `public/CNAME` preserves the `reme.agentscope.io` custom domain.
diff --git a/github-pages/index.html b/github-pages/index.html
deleted file mode 100644
index 3655d5ee..00000000
--- a/github-pages/index.html
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
- ReMe Documentation
-
-
-
-
-
-
diff --git a/github-pages/package-lock.json b/github-pages/package-lock.json
index 726cc77f..48930a39 100644
--- a/github-pages/package-lock.json
+++ b/github-pages/package-lock.json
@@ -7,21 +7,377 @@
"": {
"name": "reme-github-pages",
"version": "0.1.0",
- "dependencies": {
- "dompurify": "^3.2.6",
- "marked": "^16.2.1"
- },
"devDependencies": {
- "vite": "^7.1.1"
+ "vitepress": "1.6.4",
+ "yaml": "^2.8.1"
},
"engines": {
"node": ">=22.13.0"
}
},
+ "node_modules/@algolia/abtesting": {
+ "version": "1.23.0",
+ "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.23.0.tgz",
+ "integrity": "sha512-j45MBISstltys9QyQ4xf6quRiN1g7vMuwQL9VM4dx8YuRZvCQ173b9royZAx6iAbRX3IB1VnG1z//NuwyQ8jpQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.57.0",
+ "@algolia/requester-browser-xhr": "5.57.0",
+ "@algolia/requester-fetch": "5.57.0",
+ "@algolia/requester-node-http": "5.57.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/autocomplete-core": {
+ "version": "1.17.7",
+ "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz",
+ "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/autocomplete-plugin-algolia-insights": "1.17.7",
+ "@algolia/autocomplete-shared": "1.17.7"
+ }
+ },
+ "node_modules/@algolia/autocomplete-plugin-algolia-insights": {
+ "version": "1.17.7",
+ "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz",
+ "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/autocomplete-shared": "1.17.7"
+ },
+ "peerDependencies": {
+ "search-insights": ">= 1 < 3"
+ }
+ },
+ "node_modules/@algolia/autocomplete-preset-algolia": {
+ "version": "1.17.7",
+ "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz",
+ "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/autocomplete-shared": "1.17.7"
+ },
+ "peerDependencies": {
+ "@algolia/client-search": ">= 4.9.1 < 6",
+ "algoliasearch": ">= 4.9.1 < 6"
+ }
+ },
+ "node_modules/@algolia/autocomplete-shared": {
+ "version": "1.17.7",
+ "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz",
+ "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@algolia/client-search": ">= 4.9.1 < 6",
+ "algoliasearch": ">= 4.9.1 < 6"
+ }
+ },
+ "node_modules/@algolia/client-abtesting": {
+ "version": "5.57.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.57.0.tgz",
+ "integrity": "sha512-JVFFujiZUCguk5tz3LZr4fTQxqpIrj4/Jw3SI7kMljSqtfLxYn/s/TWH0J2s4iNfsDpxPhgFGMotCpmDI4kZ8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.57.0",
+ "@algolia/requester-browser-xhr": "5.57.0",
+ "@algolia/requester-fetch": "5.57.0",
+ "@algolia/requester-node-http": "5.57.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/client-analytics": {
+ "version": "5.57.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.57.0.tgz",
+ "integrity": "sha512-6KqECK4ED3JJQEoDrQWnGPQzElA828xAD4qK5ceawNNyP/LcSvzAoLHjFkoTPksZ/kxj6VUtCRH+IHZesLltng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.57.0",
+ "@algolia/requester-browser-xhr": "5.57.0",
+ "@algolia/requester-fetch": "5.57.0",
+ "@algolia/requester-node-http": "5.57.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/client-common": {
+ "version": "5.57.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.57.0.tgz",
+ "integrity": "sha512-uqpGF3oXYsoCbQq5d7BzNrNTfIfuvJyGP1CKvSW27T9boUg7KOwyxsAw1AX0a3jSW2HrYEJ/NN+Z4MiGivbpeQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/client-insights": {
+ "version": "5.57.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.57.0.tgz",
+ "integrity": "sha512-u5NboJVJXDEFplvNnqqX4CxkXPYysjJRj47hOSh9329H8kG5gFLKJBIiS5utMQ+GZm8xQl3Te7NInDk6elEADQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.57.0",
+ "@algolia/requester-browser-xhr": "5.57.0",
+ "@algolia/requester-fetch": "5.57.0",
+ "@algolia/requester-node-http": "5.57.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/client-personalization": {
+ "version": "5.57.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.57.0.tgz",
+ "integrity": "sha512-uzc0b2LmHAK9/QID4xeo35OG84AkZl4YewkCqawqAOGLjT2eZpM/OZx45ESygMHG30Ws+ZTSdluPtMJcUnrbWQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.57.0",
+ "@algolia/requester-browser-xhr": "5.57.0",
+ "@algolia/requester-fetch": "5.57.0",
+ "@algolia/requester-node-http": "5.57.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/client-query-suggestions": {
+ "version": "5.57.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.57.0.tgz",
+ "integrity": "sha512-dIAhnM6ue/ssa5PjgNfu4g8A4yTojl9ZOUzZU3wIaIKRerL2R/3Emuf9n/D6ICXXP167KC6XCeC7nliSw7cuSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.57.0",
+ "@algolia/requester-browser-xhr": "5.57.0",
+ "@algolia/requester-fetch": "5.57.0",
+ "@algolia/requester-node-http": "5.57.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/client-search": {
+ "version": "5.57.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.57.0.tgz",
+ "integrity": "sha512-2TTPTTKSJmCptvhCm4Xf3bBYMqZni+Pgc2hVdqc4l9wsBpSJNVTVIKpnd10OubUgkGcmppVDj1XQqYaf6EnPSQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.57.0",
+ "@algolia/requester-browser-xhr": "5.57.0",
+ "@algolia/requester-fetch": "5.57.0",
+ "@algolia/requester-node-http": "5.57.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/ingestion": {
+ "version": "1.57.0",
+ "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.57.0.tgz",
+ "integrity": "sha512-W4JseHKt+pzOxlFV+T3MWEG0h4Z2Se5zjoXUD0ewlw8aOWMG/yjRdopUdLQsXULepB/My2tDuZjkwk2sMfsUrQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.57.0",
+ "@algolia/requester-browser-xhr": "5.57.0",
+ "@algolia/requester-fetch": "5.57.0",
+ "@algolia/requester-node-http": "5.57.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/monitoring": {
+ "version": "1.57.0",
+ "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.57.0.tgz",
+ "integrity": "sha512-BrxJVE0/eLinEPICCD7BKN/2xnt0nkjge70u8zzE2ISP3fuB3tjLgcwpanUycvlHBFLI4gK0l5ol54p6IYuR/Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.57.0",
+ "@algolia/requester-browser-xhr": "5.57.0",
+ "@algolia/requester-fetch": "5.57.0",
+ "@algolia/requester-node-http": "5.57.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/recommend": {
+ "version": "5.57.0",
+ "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.57.0.tgz",
+ "integrity": "sha512-Gc29jkeiLKlVfHvyrIgyUHHE+aYTdXEeLfK42rjr5/1TTVsYwUJz0XkvoIBIqfMjcDg6gXeHb1jTUZ0H+SYIlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.57.0",
+ "@algolia/requester-browser-xhr": "5.57.0",
+ "@algolia/requester-fetch": "5.57.0",
+ "@algolia/requester-node-http": "5.57.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/requester-browser-xhr": {
+ "version": "5.57.0",
+ "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.57.0.tgz",
+ "integrity": "sha512-PIPnPN7MP3fp2VAi01BVXhCWmD366ZB2Hkq5TlYKtThd4KxUtMmaaNDpFgVCTXtSIqWVZLJntOHRvxg/sIPd8Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.57.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/requester-fetch": {
+ "version": "5.57.0",
+ "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.57.0.tgz",
+ "integrity": "sha512-AX3RlOudXMdTwtwUqdAf5hAVLvXfOZZH1FZh6ALDdrhVLT0TtAIe48N6nYcWcTnwoTxK/wDIQqZ19IMjK8zJAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.57.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/requester-node-http": {
+ "version": "5.57.0",
+ "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.57.0.tgz",
+ "integrity": "sha512-cWZc1dKb7wy9/wPpwMtL1y89gK2G7y2A47Coa7zwf1ydtIeJm4+S+XxoQ2b/ZRiQnrC1YHavLjYUPDCdnT7Khg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.57.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz",
+ "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.8"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",
+ "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@docsearch/css": {
+ "version": "3.8.2",
+ "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz",
+ "integrity": "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@docsearch/js": {
+ "version": "3.8.2",
+ "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz",
+ "integrity": "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docsearch/react": "3.8.2",
+ "preact": "^10.0.0"
+ }
+ },
+ "node_modules/@docsearch/react": {
+ "version": "3.8.2",
+ "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz",
+ "integrity": "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/autocomplete-core": "1.17.7",
+ "@algolia/autocomplete-preset-algolia": "1.17.7",
+ "@docsearch/css": "3.8.2",
+ "algoliasearch": "^5.14.2"
+ },
+ "peerDependencies": {
+ "@types/react": ">= 16.8.0 < 19.0.0",
+ "react": ">= 16.8.0 < 19.0.0",
+ "react-dom": ">= 16.8.0 < 19.0.0",
+ "search-insights": ">= 1 < 3"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ },
+ "search-insights": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
- "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
"cpu": [
"ppc64"
],
@@ -32,13 +388,13 @@
"aix"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
- "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
"cpu": [
"arm"
],
@@ -49,13 +405,13 @@
"android"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
- "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
"cpu": [
"arm64"
],
@@ -66,13 +422,13 @@
"android"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
- "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
"cpu": [
"x64"
],
@@ -83,13 +439,13 @@
"android"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
- "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
"cpu": [
"arm64"
],
@@ -100,13 +456,13 @@
"darwin"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
- "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
"cpu": [
"x64"
],
@@ -117,13 +473,13 @@
"darwin"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
- "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
"cpu": [
"arm64"
],
@@ -134,13 +490,13 @@
"freebsd"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
- "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
"cpu": [
"x64"
],
@@ -151,13 +507,13 @@
"freebsd"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
- "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
"cpu": [
"arm"
],
@@ -168,13 +524,13 @@
"linux"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
- "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
"cpu": [
"arm64"
],
@@ -185,13 +541,13 @@
"linux"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
- "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
"cpu": [
"ia32"
],
@@ -202,13 +558,13 @@
"linux"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
- "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
"cpu": [
"loong64"
],
@@ -219,13 +575,13 @@
"linux"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
- "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
"cpu": [
"mips64el"
],
@@ -236,13 +592,13 @@
"linux"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
- "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
"cpu": [
"ppc64"
],
@@ -253,13 +609,13 @@
"linux"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
- "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
"cpu": [
"riscv64"
],
@@ -270,13 +626,13 @@
"linux"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
- "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
"cpu": [
"s390x"
],
@@ -287,13 +643,13 @@
"linux"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
- "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
"cpu": [
"x64"
],
@@ -304,30 +660,13 @@
"linux"
],
"engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/netbsd-arm64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
- "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
- "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
"cpu": [
"x64"
],
@@ -338,30 +677,13 @@
"netbsd"
],
"engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openbsd-arm64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
- "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
- "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
"cpu": [
"x64"
],
@@ -372,30 +694,13 @@
"openbsd"
],
"engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openharmony-arm64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
- "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openharmony"
- ],
- "engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
- "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
"cpu": [
"x64"
],
@@ -406,13 +711,13 @@
"sunos"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
- "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
"cpu": [
"arm64"
],
@@ -423,13 +728,13 @@
"win32"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
- "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
"cpu": [
"ia32"
],
@@ -440,13 +745,13 @@
"win32"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
- "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
"cpu": [
"x64"
],
@@ -457,9 +762,33 @@
"win32"
],
"engines": {
- "node": ">=18"
+ "node": ">=12"
}
},
+ "node_modules/@iconify-json/simple-icons": {
+ "version": "1.2.94",
+ "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.94.tgz",
+ "integrity": "sha512-l8UWzVxKaqZd9ABsE/M/9p6NyGkQnmCnOoZyhQmjlXCtY5PuL2rcWxOFk2l9pk7ux3ERMPkTLE4jl6kQpTkwxA==",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "@iconify/types": "*"
+ }
+ },
+ "node_modules/@iconify/types": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
+ "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz",
+ "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@napi-rs/lzma-linux-x64-gnu": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz",
@@ -827,6 +1156,93 @@
"win32"
]
},
+ "node_modules/@shikijs/core": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz",
+ "integrity": "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/engine-javascript": "2.5.0",
+ "@shikijs/engine-oniguruma": "2.5.0",
+ "@shikijs/types": "2.5.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4",
+ "hast-util-to-html": "^9.0.4"
+ }
+ },
+ "node_modules/@shikijs/engine-javascript": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz",
+ "integrity": "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "2.5.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "oniguruma-to-es": "^3.1.0"
+ }
+ },
+ "node_modules/@shikijs/engine-oniguruma": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz",
+ "integrity": "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "2.5.0",
+ "@shikijs/vscode-textmate": "^10.0.2"
+ }
+ },
+ "node_modules/@shikijs/langs": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz",
+ "integrity": "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "2.5.0"
+ }
+ },
+ "node_modules/@shikijs/themes": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz",
+ "integrity": "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "2.5.0"
+ }
+ },
+ "node_modules/@shikijs/transformers": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.5.0.tgz",
+ "integrity": "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/core": "2.5.0",
+ "@shikijs/types": "2.5.0"
+ }
+ },
+ "node_modules/@shikijs/types": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz",
+ "integrity": "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4"
+ }
+ },
+ "node_modules/@shikijs/vscode-textmate": {
+ "version": "10.0.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz",
+ "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/estree": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
@@ -834,26 +1250,483 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@types/trusted-types": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
- "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+ "node_modules/@types/hast": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz",
+ "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==",
+ "dev": true,
"license": "MIT",
- "optional": true
+ "dependencies": {
+ "@types/unist": "*"
+ }
},
- "node_modules/dompurify": {
- "version": "3.4.13",
- "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.13.tgz",
- "integrity": "sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==",
- "license": "(MPL-2.0 OR Apache-2.0)",
- "optionalDependencies": {
- "@types/trusted-types": "^2.0.7"
+ "node_modules/@types/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/markdown-it": {
+ "version": "14.2.0",
+ "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.2.0.tgz",
+ "integrity": "sha512-NoQ2yGlLWj4wpxMs+TYmRKk3thDrQ97agr7sFqfLsAlvoS8SNQuTrlObhFqG9iugdTtgOE9jpJ6FNM4ZGsa5xQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/linkify-it": "^5",
+ "@types/mdurl": "^2"
+ }
+ },
+ "node_modules/@types/mdast": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
+ "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/unist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/web-bluetooth": {
+ "version": "0.0.21",
+ "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
+ "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz",
+ "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/@vitejs/plugin-vue": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz",
+ "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^5.0.0 || ^6.0.0",
+ "vue": "^3.2.25"
+ }
+ },
+ "node_modules/@vue/compiler-core": {
+ "version": "3.5.42",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.42.tgz",
+ "integrity": "sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.8",
+ "@vue/shared": "3.5.42",
+ "entities": "^7.0.1",
+ "estree-walker": "^2.0.2",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/@vue/compiler-dom": {
+ "version": "3.5.42",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.42.tgz",
+ "integrity": "sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-core": "3.5.42",
+ "@vue/shared": "3.5.42"
+ }
+ },
+ "node_modules/@vue/compiler-sfc": {
+ "version": "3.5.42",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.42.tgz",
+ "integrity": "sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.8",
+ "@vue/compiler-core": "3.5.42",
+ "@vue/compiler-dom": "3.5.42",
+ "@vue/compiler-ssr": "3.5.42",
+ "@vue/shared": "3.5.42",
+ "estree-walker": "^2.0.2",
+ "magic-string": "^0.30.21",
+ "postcss": "^8.5.19",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/@vue/compiler-ssr": {
+ "version": "3.5.42",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.42.tgz",
+ "integrity": "sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.42",
+ "@vue/shared": "3.5.42"
+ }
+ },
+ "node_modules/@vue/devtools-api": {
+ "version": "7.7.10",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.10.tgz",
+ "integrity": "sha512-KxtEpUOOpFz/qOGRrAwA36QF7DqIA+FXgCYit9mk9wjbaZt0sXOFz81ElOZtKA4HbWHUdwNjZHBFsFFyp5BZiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-kit": "^7.7.10"
+ }
+ },
+ "node_modules/@vue/devtools-kit": {
+ "version": "7.7.10",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.10.tgz",
+ "integrity": "sha512-3WNi2Kq4tbpVbmhml7RiphmAt0279oh3fKNeWMQIrltfX8Q91b4i5PL8DtyNKdwmcsGrV4fg+erwWOmD05CLIw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-shared": "^7.7.10",
+ "birpc": "^2.3.0",
+ "hookable": "^5.5.3",
+ "mitt": "^3.0.1",
+ "perfect-debounce": "^1.0.0",
+ "speakingurl": "^14.0.1",
+ "superjson": "^2.2.2"
+ }
+ },
+ "node_modules/@vue/devtools-shared": {
+ "version": "7.7.10",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.10.tgz",
+ "integrity": "sha512-wOPslzB8vTvpxwdaOcR2qAbwmuSP0L+rhpoC6Cf56V3Jip+HWb7PQQXOUPgBNQARpXsbQX/+mvi8kKucmBGRwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "rfdc": "^1.4.1"
+ }
+ },
+ "node_modules/@vue/reactivity": {
+ "version": "3.5.42",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.42.tgz",
+ "integrity": "sha512-TzNNfKpb7hDxbQltwAut8VDQA5YP+BuRlxntHUuRjyKwlMvmAPbs3unhCvieijifY6vFfVBwsS7wG/C7uq+bEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/shared": "3.5.42"
+ }
+ },
+ "node_modules/@vue/runtime-core": {
+ "version": "3.5.42",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.42.tgz",
+ "integrity": "sha512-9uACtuHs7vJGkm5Bp3xu4xRDLFTIYy5DgxpToVjqGIAhAEKwQfsaLvKINhM6nFVp6bZPRFGdDqd1g52MqKsotA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "3.5.42",
+ "@vue/shared": "3.5.42"
+ }
+ },
+ "node_modules/@vue/runtime-dom": {
+ "version": "3.5.42",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.42.tgz",
+ "integrity": "sha512-rsCmhiWLaRxGltLwhlCWyYkFn7WAbKRh0q17eZ1A6Dq6eqc2ACQ61IIryxz0LrsvCzHSilLA9JHovVwM8CNE2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "3.5.42",
+ "@vue/runtime-core": "3.5.42",
+ "@vue/shared": "3.5.42",
+ "csstype": "^3.2.3"
+ }
+ },
+ "node_modules/@vue/server-renderer": {
+ "version": "3.5.42",
+ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.42.tgz",
+ "integrity": "sha512-2++5dUyYS4gvo7xQXSECUDhB7TS0aOl5SeVfC5qSq1Jgfhjvegw1zqhwTIR3imZ+QYPJQw9gfcFvXGAjGZ7ajQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-ssr": "3.5.42",
+ "@vue/runtime-dom": "3.5.42",
+ "@vue/shared": "3.5.42"
+ }
+ },
+ "node_modules/@vue/shared": {
+ "version": "3.5.42",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.42.tgz",
+ "integrity": "sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@vueuse/core": {
+ "version": "12.8.2",
+ "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz",
+ "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/web-bluetooth": "^0.0.21",
+ "@vueuse/metadata": "12.8.2",
+ "@vueuse/shared": "12.8.2",
+ "vue": "^3.5.13"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/@vueuse/integrations": {
+ "version": "12.8.2",
+ "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz",
+ "integrity": "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vueuse/core": "12.8.2",
+ "@vueuse/shared": "12.8.2",
+ "vue": "^3.5.13"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "async-validator": "^4",
+ "axios": "^1",
+ "change-case": "^5",
+ "drauu": "^0.4",
+ "focus-trap": "^7",
+ "fuse.js": "^7",
+ "idb-keyval": "^6",
+ "jwt-decode": "^4",
+ "nprogress": "^0.2",
+ "qrcode": "^1.5",
+ "sortablejs": "^1",
+ "universal-cookie": "^7"
+ },
+ "peerDependenciesMeta": {
+ "async-validator": {
+ "optional": true
+ },
+ "axios": {
+ "optional": true
+ },
+ "change-case": {
+ "optional": true
+ },
+ "drauu": {
+ "optional": true
+ },
+ "focus-trap": {
+ "optional": true
+ },
+ "fuse.js": {
+ "optional": true
+ },
+ "idb-keyval": {
+ "optional": true
+ },
+ "jwt-decode": {
+ "optional": true
+ },
+ "nprogress": {
+ "optional": true
+ },
+ "qrcode": {
+ "optional": true
+ },
+ "sortablejs": {
+ "optional": true
+ },
+ "universal-cookie": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vueuse/metadata": {
+ "version": "12.8.2",
+ "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz",
+ "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/@vueuse/shared": {
+ "version": "12.8.2",
+ "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz",
+ "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "vue": "^3.5.13"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/algoliasearch": {
+ "version": "5.57.0",
+ "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.57.0.tgz",
+ "integrity": "sha512-HpND7MBGctOAkd1GoQoDZCGoCpqNTS5NG1LuhElFet3RdLJkwnyTYZXZhXwtpAQPrI36fqQ3eT6KQrdKDTKu3A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/abtesting": "1.23.0",
+ "@algolia/client-abtesting": "5.57.0",
+ "@algolia/client-analytics": "5.57.0",
+ "@algolia/client-common": "5.57.0",
+ "@algolia/client-insights": "5.57.0",
+ "@algolia/client-personalization": "5.57.0",
+ "@algolia/client-query-suggestions": "5.57.0",
+ "@algolia/client-search": "5.57.0",
+ "@algolia/ingestion": "1.57.0",
+ "@algolia/monitoring": "1.57.0",
+ "@algolia/recommend": "5.57.0",
+ "@algolia/requester-browser-xhr": "5.57.0",
+ "@algolia/requester-fetch": "5.57.0",
+ "@algolia/requester-node-http": "5.57.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/birpc": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz",
+ "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/ccount": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
+ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-html4": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
+ "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-legacy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/comma-separated-tokens": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
+ "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/copy-anything": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.1.0.tgz",
+ "integrity": "sha512-ufbM3smX/Jbnpk5wcQjzd1MgBpzmqfNETUAyZNrGwU9foRlyHoGzMMBBCRzEhQLBjZfFDE1W2ufPXX2vdWkV8Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mesqueeb"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/devlop": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/emoji-regex-xs": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz",
+ "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/entities": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
+ "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/esbuild": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
- "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -861,53 +1734,49 @@
"esbuild": "bin/esbuild"
},
"engines": {
- "node": ">=18"
+ "node": ">=12"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.28.2",
- "@esbuild/android-arm": "0.28.2",
- "@esbuild/android-arm64": "0.28.2",
- "@esbuild/android-x64": "0.28.2",
- "@esbuild/darwin-arm64": "0.28.2",
- "@esbuild/darwin-x64": "0.28.2",
- "@esbuild/freebsd-arm64": "0.28.2",
- "@esbuild/freebsd-x64": "0.28.2",
- "@esbuild/linux-arm": "0.28.2",
- "@esbuild/linux-arm64": "0.28.2",
- "@esbuild/linux-ia32": "0.28.2",
- "@esbuild/linux-loong64": "0.28.2",
- "@esbuild/linux-mips64el": "0.28.2",
- "@esbuild/linux-ppc64": "0.28.2",
- "@esbuild/linux-riscv64": "0.28.2",
- "@esbuild/linux-s390x": "0.28.2",
- "@esbuild/linux-x64": "0.28.2",
- "@esbuild/netbsd-arm64": "0.28.2",
- "@esbuild/netbsd-x64": "0.28.2",
- "@esbuild/openbsd-arm64": "0.28.2",
- "@esbuild/openbsd-x64": "0.28.2",
- "@esbuild/openharmony-arm64": "0.28.2",
- "@esbuild/sunos-x64": "0.28.2",
- "@esbuild/win32-arm64": "0.28.2",
- "@esbuild/win32-ia32": "0.28.2",
- "@esbuild/win32-x64": "0.28.2"
+ "@esbuild/aix-ppc64": "0.21.5",
+ "@esbuild/android-arm": "0.21.5",
+ "@esbuild/android-arm64": "0.21.5",
+ "@esbuild/android-x64": "0.21.5",
+ "@esbuild/darwin-arm64": "0.21.5",
+ "@esbuild/darwin-x64": "0.21.5",
+ "@esbuild/freebsd-arm64": "0.21.5",
+ "@esbuild/freebsd-x64": "0.21.5",
+ "@esbuild/linux-arm": "0.21.5",
+ "@esbuild/linux-arm64": "0.21.5",
+ "@esbuild/linux-ia32": "0.21.5",
+ "@esbuild/linux-loong64": "0.21.5",
+ "@esbuild/linux-mips64el": "0.21.5",
+ "@esbuild/linux-ppc64": "0.21.5",
+ "@esbuild/linux-riscv64": "0.21.5",
+ "@esbuild/linux-s390x": "0.21.5",
+ "@esbuild/linux-x64": "0.21.5",
+ "@esbuild/netbsd-x64": "0.21.5",
+ "@esbuild/openbsd-x64": "0.21.5",
+ "@esbuild/sunos-x64": "0.21.5",
+ "@esbuild/win32-arm64": "0.21.5",
+ "@esbuild/win32-ia32": "0.21.5",
+ "@esbuild/win32-x64": "0.21.5"
}
},
- "node_modules/fdir": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
- "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/focus-trap": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.8.0.tgz",
+ "integrity": "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==",
"dev": true,
"license": "MIT",
- "engines": {
- "node": ">=12.0.0"
- },
- "peerDependencies": {
- "picomatch": "^3 || ^4"
- },
- "peerDependenciesMeta": {
- "picomatch": {
- "optional": true
- }
+ "dependencies": {
+ "tabbable": "^6.4.0"
}
},
"node_modules/fsevents": {
@@ -925,18 +1794,209 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
- "node_modules/marked": {
- "version": "16.4.2",
- "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz",
- "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==",
+ "node_modules/hast-util-to-html": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz",
+ "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==",
+ "dev": true,
"license": "MIT",
- "bin": {
- "marked": "bin/marked.js"
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "html-void-elements": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "stringify-entities": "^4.0.0",
+ "zwitch": "^2.0.4"
},
- "engines": {
- "node": ">= 20"
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
+ "node_modules/hast-util-whitespace": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
+ "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hookable": {
+ "version": "5.5.3",
+ "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
+ "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/html-void-elements": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
+ "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/mark.js": {
+ "version": "8.11.1",
+ "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz",
+ "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mdast-util-to-hast": {
+ "version": "13.2.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
+ "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "trim-lines": "^3.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-encode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
+ "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-sanitize-uri": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
+ "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-types": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
+ "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/minisearch": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz",
+ "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mitt": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/nanoid": {
"version": "3.3.18",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
@@ -956,6 +2016,25 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
+ "node_modules/oniguruma-to-es": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz",
+ "integrity": "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex-xs": "^1.0.0",
+ "regex": "^6.0.1",
+ "regex-recursion": "^6.0.2"
+ }
+ },
+ "node_modules/perfect-debounce": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
+ "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -963,19 +2042,6 @@
"dev": true,
"license": "ISC"
},
- "node_modules/picomatch": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
- "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
"node_modules/postcss": {
"version": "8.5.26",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz",
@@ -1005,6 +2071,70 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/preact": {
+ "version": "10.29.8",
+ "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.8.tgz",
+ "integrity": "sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/preact"
+ },
+ "peerDependencies": {
+ "preact-render-to-string": ">=5"
+ },
+ "peerDependenciesMeta": {
+ "preact-render-to-string": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/property-information": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz",
+ "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz",
+ "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "regex-utilities": "^2.3.0"
+ }
+ },
+ "node_modules/regex-recursion": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz",
+ "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "regex-utilities": "^2.3.0"
+ }
+ },
+ "node_modules/regex-utilities": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz",
+ "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/rollup": {
"version": "4.62.4",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz",
@@ -1051,6 +2181,31 @@
"fsevents": "~2.3.2"
}
},
+ "node_modules/search-insights": {
+ "version": "2.17.3",
+ "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz",
+ "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/shiki": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz",
+ "integrity": "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/core": "2.5.0",
+ "@shikijs/engine-javascript": "2.5.0",
+ "@shikijs/engine-oniguruma": "2.5.0",
+ "@shikijs/langs": "2.5.0",
+ "@shikijs/themes": "2.5.0",
+ "@shikijs/types": "2.5.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4"
+ }
+ },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -1061,42 +2216,192 @@
"node": ">=0.10.0"
}
},
- "node_modules/tinyglobby": {
- "version": "0.2.17",
- "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
- "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+ "node_modules/space-separated-tokens": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
+ "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/speakingurl": {
+ "version": "14.0.1",
+ "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
+ "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stringify-entities": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
+ "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "fdir": "^6.5.0",
- "picomatch": "^4.0.4"
- },
- "engines": {
- "node": ">=12.0.0"
+ "character-entities-html4": "^2.0.0",
+ "character-entities-legacy": "^3.0.0"
},
"funding": {
- "url": "https://github.com/sponsors/SuperchupuDev"
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/superjson": {
+ "version": "2.2.6",
+ "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz",
+ "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "copy-anything": "^4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/tabbable": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.5.0.tgz",
+ "integrity": "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/trim-lines": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
+ "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/unist-util-is": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz",
+ "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-position": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
+ "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-stringify-position": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz",
+ "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-parents": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz",
+ "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
+ "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-message": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz",
+ "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
"node_modules/vite": {
- "version": "7.3.6",
- "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz",
- "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==",
+ "version": "5.4.21",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
+ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "esbuild": "^0.27.0 || ^0.28.0",
- "fdir": "^6.5.0",
- "picomatch": "^4.0.3",
- "postcss": "^8.5.6",
- "rollup": "^4.43.0",
- "tinyglobby": "^0.2.15"
+ "esbuild": "^0.21.3",
+ "postcss": "^8.4.43",
+ "rollup": "^4.20.0"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "node": "^18.0.0 || >=20.0.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
@@ -1105,25 +2410,19 @@
"fsevents": "~2.3.3"
},
"peerDependencies": {
- "@types/node": "^20.19.0 || >=22.12.0",
- "jiti": ">=1.21.0",
- "less": "^4.0.0",
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "less": "*",
"lightningcss": "^1.21.0",
- "sass": "^1.70.0",
- "sass-embedded": "^1.70.0",
- "stylus": ">=0.54.8",
- "sugarss": "^5.0.0",
- "terser": "^5.16.0",
- "tsx": "^4.8.1",
- "yaml": "^2.4.2"
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
- "jiti": {
- "optional": true
- },
"less": {
"optional": true
},
@@ -1144,14 +2443,99 @@
},
"terser": {
"optional": true
- },
- "tsx": {
+ }
+ }
+ },
+ "node_modules/vitepress": {
+ "version": "1.6.4",
+ "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.4.tgz",
+ "integrity": "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@docsearch/css": "3.8.2",
+ "@docsearch/js": "3.8.2",
+ "@iconify-json/simple-icons": "^1.2.21",
+ "@shikijs/core": "^2.1.0",
+ "@shikijs/transformers": "^2.1.0",
+ "@shikijs/types": "^2.1.0",
+ "@types/markdown-it": "^14.1.2",
+ "@vitejs/plugin-vue": "^5.2.1",
+ "@vue/devtools-api": "^7.7.0",
+ "@vue/shared": "^3.5.13",
+ "@vueuse/core": "^12.4.0",
+ "@vueuse/integrations": "^12.4.0",
+ "focus-trap": "^7.6.4",
+ "mark.js": "8.11.1",
+ "minisearch": "^7.1.1",
+ "shiki": "^2.1.0",
+ "vite": "^5.4.14",
+ "vue": "^3.5.13"
+ },
+ "bin": {
+ "vitepress": "bin/vitepress.js"
+ },
+ "peerDependencies": {
+ "markdown-it-mathjax3": "^4",
+ "postcss": "^8"
+ },
+ "peerDependenciesMeta": {
+ "markdown-it-mathjax3": {
"optional": true
},
- "yaml": {
+ "postcss": {
"optional": true
}
}
+ },
+ "node_modules/vue": {
+ "version": "3.5.42",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.42.tgz",
+ "integrity": "sha512-4RyHQTbQvOPs3MfvUO1Sg0YRrKNnA0mAVtvpd12Tg1fKDN7OHBUl1IqSn8zGJjK9nI3NkNp8cgTpVrSZC5TTcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.42",
+ "@vue/compiler-sfc": "3.5.42",
+ "@vue/runtime-dom": "3.5.42",
+ "@vue/server-renderer": "3.5.42",
+ "@vue/shared": "3.5.42"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/yaml": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
+ "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
+ }
+ },
+ "node_modules/zwitch": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+ "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
}
}
}
diff --git a/github-pages/package.json b/github-pages/package.json
index 4ace932f..a9a0eb6d 100644
--- a/github-pages/package.json
+++ b/github-pages/package.json
@@ -7,16 +7,14 @@
"node": ">=22.13.0"
},
"scripts": {
- "dev": "node scripts/generate-content.mjs && vite",
- "build": "node scripts/generate-content.mjs && vite build",
- "preview": "vite preview",
- "test": "node scripts/generate-content.mjs && node --test tests/*.test.mjs"
- },
- "dependencies": {
- "dompurify": "^3.2.6",
- "marked": "^16.2.1"
+ "content": "node scripts/generate-content.mjs",
+ "dev": "npm run content && vitepress dev .generated/site",
+ "build": "npm run content && vitepress build .generated/site --outDir dist && node scripts/verify-build.mjs",
+ "preview": "vitepress preview .generated/site --outDir dist",
+ "test": "npm run content && node --test tests/*.test.mjs"
},
"devDependencies": {
- "vite": "^7.1.1"
+ "vitepress": "1.6.4",
+ "yaml": "^2.8.1"
}
}
diff --git a/github-pages/scripts/generate-content.mjs b/github-pages/scripts/generate-content.mjs
index 05054730..46608a1a 100644
--- a/github-pages/scripts/generate-content.mjs
+++ b/github-pages/scripts/generate-content.mjs
@@ -1,249 +1,236 @@
import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
+import { parse as parseYaml } from "yaml";
const siteDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const repoDir = path.resolve(siteDir, "..");
-const outputDir = path.join(siteDir, ".generated", "content");
+const outputDir = path.join(siteDir, ".generated", "site");
-const navigationGroupOrder = [
- "overview",
- "start",
- "integration",
- "fundamentals",
- "automation",
- "concepts",
- "workspace",
- "plugins",
- "benchmarks",
- "development",
+const externalDocuments = [
+ ["en/integrations/claude-code.md", "integrations/claude_code/README.md"],
+ ["en/integrations/hermes.md", "integrations/hermes_agent/README.md"],
+ ["zh/integrations/typescript.md", "typescript/README_ZH.md"],
+ ["en/integrations/typescript.md", "typescript/README.md"],
+ ["zh/integrations/dsh.md", "typescript/docs/dsh.zh-CN.md"],
+ ["en/integrations/dsh.md", "typescript/docs/dsh.md"],
+ ["zh/integrations/openclaw.md", "typescript/docs/openclaw.zh-CN.md"],
+ ["en/integrations/openclaw.md", "typescript/docs/openclaw.md"],
+ ["zh/workspace/studio.md", "reme_studio/README_ZH.md"],
+ ["en/workspace/studio.md", "reme_studio/README.md"],
+ ["zh/plugins/daily-paper.md", "plugins/daily_paper/README_ZH.md"],
+ ["en/plugins/daily-paper.md", "plugins/daily_paper/README.md"],
+ ["zh/plugins/auto-fin.md", "plugins/auto-fin/README_ZH.md"],
+ ["en/plugins/auto-fin.md", "plugins/auto-fin/README.md"],
+ ["zh/plugins/lme.md", "plugins/lme/README_ZH.md"],
+ ["en/plugins/lme.md", "plugins/lme/README.md"],
+ ["zh/plugins/beam.md", "plugins/beam/README_ZH.md"],
+ ["en/plugins/beam.md", "plugins/beam/README.md"],
+ ["zh/benchmarks/beam.md", "benchmark/beam/README_ZH.md"],
+ ["en/benchmarks/beam.md", "benchmark/beam/README.md"],
+ ["zh/benchmarks/longmemeval.md", "benchmark/longmemeval/README_ZH.md"],
+ ["en/benchmarks/longmemeval.md", "benchmark/longmemeval/README.md"],
+ ["zh/benchmarks/pibench.md", "benchmark/pibench/README_ZH.md"],
+ ["en/benchmarks/pibench.md", "benchmark/pibench/README.md"],
+ ["zh/benchmarks/toolmemory.md", "benchmark/toolmemory/README_ZH.md"],
+ ["en/benchmarks/toolmemory.md", "benchmark/toolmemory/README.md"],
];
-const topicOrder = [
- "quick_start",
- "plugin_management",
- "memory_as_file",
- "memory_search",
- "auto_memory",
- "auto_resource",
- "auto_link",
- "auto_dream",
- "proactive",
- "reme_scene",
- "framework",
- "reme-blog",
- "contributing",
-];
-
-const groups = {
- quick_start: "start",
- plugin_management: "start",
- memory_as_file: "fundamentals",
- memory_search: "fundamentals",
- auto_memory: "automation",
- auto_resource: "automation",
- auto_link: "automation",
- auto_dream: "automation",
- proactive: "automation",
- reme_scene: "concepts",
- framework: "concepts",
- "reme-blog": "concepts",
- contributing: "development",
+const externalDocumentRewrites = {
+ "typescript/README.md": [
+ ["(./README_ZH.md)", "(/zh/integrations/typescript)"],
+ ["(./docs/dsh.md)", "(/en/integrations/dsh)"],
+ ["(./docs/dsh.zh-CN.md)", "(/zh/integrations/dsh)"],
+ ["(./docs/openclaw.md)", "(/en/integrations/openclaw)"],
+ ["(./docs/openclaw.zh-CN.md)", "(/zh/integrations/openclaw)"],
+ ["(./figures/dsh/", "(/figures/dsh/"],
+ ],
+ "typescript/README_ZH.md": [
+ ["(./README.md)", "(/en/integrations/typescript)"],
+ ["(./docs/dsh.md)", "(/en/integrations/dsh)"],
+ ["(./docs/dsh.zh-CN.md)", "(/zh/integrations/dsh)"],
+ ["(./docs/openclaw.md)", "(/en/integrations/openclaw)"],
+ ["(./docs/openclaw.zh-CN.md)", "(/zh/integrations/openclaw)"],
+ ["(./figures/dsh/", "(/figures/dsh/"],
+ ],
+ "typescript/docs/dsh.md": [
+ ["(./dsh.zh-CN.md)", "(/zh/integrations/dsh)"],
+ ["(../figures/dsh/", "(/figures/dsh/"],
+ ],
+ "typescript/docs/dsh.zh-CN.md": [
+ ["(./dsh.md)", "(/en/integrations/dsh)"],
+ ["(../figures/dsh/", "(/figures/dsh/"],
+ ],
+ "typescript/docs/openclaw.md": [
+ ["(./openclaw.zh-CN.md)", "(/zh/integrations/openclaw)"],
+ ],
+ "typescript/docs/openclaw.zh-CN.md": [
+ ["(./openclaw.md)", "(/en/integrations/openclaw)"],
+ ],
};
-const localizedTitles = {
- quick_start: { zh: "快速开始", en: "Quick Start" },
- plugin_management: { zh: "插件管理", en: "Plugin Management" },
- memory_as_file: { zh: "文件即记忆", en: "Memory as File" },
- memory_search: { zh: "记忆检索", en: "Memory Search" },
- auto_memory: { zh: "自动记忆", en: "Auto Memory" },
- auto_resource: { zh: "自动资料整理", en: "Auto Resource" },
- auto_link: { zh: "自动关联", en: "Auto Link" },
- auto_dream: { zh: "自动沉淀", en: "Auto Dream" },
- proactive: { zh: "主动发现", en: "Proactive" },
- reme_scene: { zh: "ReMe 应用场景", en: "ReMe Application Scenarios" },
- framework: { zh: "ReMe 代码框架", en: "ReMe Framework" },
- "reme-blog": { zh: "ReMe 博客", en: "ReMe Blog" },
- contributing: { zh: "开源与贡献", en: "Open Source and Contributing" },
+const groupNames = {
+ zh: {
+ system: "系统与诊断",
+ memory: "记忆演化",
+ retrieval: "检索与图谱",
+ daily: "Daily Note",
+ files: "文件操作",
+ },
+ en: {
+ system: "System and diagnostics",
+ memory: "Memory evolution",
+ retrieval: "Retrieval and graph",
+ daily: "Daily notes",
+ files: "File operations",
+ },
};
-const productDocuments = [
- {
- slug: "typescript",
- source: "typescript",
- titles: { zh: "TypeScript Agent 集成", en: "TypeScript Agent Integrations" },
- descriptions: {
- zh: "配置统一 HTTP client,以及 DeepSeek Harness 和 OpenClaw 原生适配器。",
- en: "Configure the shared HTTP client and native DeepSeek Harness and OpenClaw adapters.",
- },
- group: "integration",
- },
- {
- slug: "studio",
- source: "reme_studio",
- titles: { zh: "ReMe 工作台", en: "ReMe Studio" },
- descriptions: {
- zh: "浏览、编辑和搜索本地记忆,并探索记忆图谱。",
- en: "Browse, edit, search, and explore local memory from the web workspace.",
- },
- group: "workspace",
- },
- {
- slug: "daily-paper",
- source: "plugins/daily_paper",
- titles: { zh: "每日论文插件", en: "Daily Paper Plugin" },
- descriptions: {
- zh: "发现论文、解析 PDF,并生成阅读笔记与每日简报。",
- en: "Discover papers, analyze PDFs, and produce reading notes and a daily brief.",
- },
- group: "plugins",
- },
- {
- slug: "auto-fin",
- source: "plugins/auto-fin",
- titles: { zh: "Auto Fin 插件", en: "Auto Fin Plugin" },
- descriptions: {
- zh: "结合最新财联社新闻与本地历史记忆生成研究报告。",
- en: "Research recent CLS news with historical context from local memory.",
- },
- group: "plugins",
- },
- {
- slug: "beam",
- source: "benchmark/beam",
- titles: { zh: "BEAM", en: "BEAM" },
- descriptions: {
- zh: "评测大规模记忆检索能力。",
- en: "Evaluate memory retrieval at scale.",
- },
- group: "benchmarks",
- },
- {
- slug: "longmemeval",
- source: "benchmark/longmemeval",
- titles: { zh: "LongMemEval", en: "LongMemEval" },
- descriptions: {
- zh: "评测跨会话长期记忆问答能力。",
- en: "Evaluate long-term, cross-session memory question answering.",
- },
- group: "benchmarks",
- },
- {
- slug: "pibench",
- source: "benchmark/pibench",
- titles: { zh: "π-Bench", en: "π-Bench" },
- descriptions: {
- zh: "评测带持久记忆的个人智能体。",
- en: "Evaluate personal agents with persistent memory.",
- },
- group: "benchmarks",
- },
- {
- slug: "toolmemory",
- source: "benchmark/toolmemory",
- titles: { zh: "Tool Memory / ExpG", en: "Tool Memory / ExpG" },
- descriptions: {
- zh: "通过经验驱动的自适应指导增强 Agent 工具使用。",
- en: "Improve agent tool use through experience-driven adaptive guidance.",
- },
- group: "benchmarks",
- },
-];
+const jobGroups = {
+ version: "system",
+ app_config: "system",
+ chat: "system",
+ health_check: "system",
+ status: "system",
+ help: "system",
+ auto_dream: "memory",
+ auto_memory: "memory",
+ auto_memory_cc: "memory",
+ auto_resource: "memory",
+ proactive: "memory",
+ traverse: "retrieval",
+ graph_snapshot: "retrieval",
+ reindex: "retrieval",
+ search: "retrieval",
+ node_search: "retrieval",
+ daily_list: "daily",
+ daily_reindex: "daily",
+ daily_write: "daily",
+ frontmatter_delete: "files",
+ frontmatter_read: "files",
+ frontmatter_update: "files",
+ stat: "files",
+ list: "files",
+ move: "files",
+ delete: "files",
+ read: "files",
+ load: "files",
+ read_image: "files",
+ write: "files",
+ save: "files",
+ edit: "files",
+};
-const sharedDocuments = [
- {
- id: "agents-guide",
- path: "AGENTS.md",
- sourcePath: "AGENTS.md",
- titles: {
- zh: "Agent 开发指南",
- en: "Agent Development Guide",
- },
- description: "Repository contracts, lifecycle rules, safety boundaries, and validation.",
- group: "development",
- language: "shared",
- },
-];
-
-async function markdownTitle(filePath) {
- const source = await readFile(filePath, "utf8");
- return source.match(/^#\s+(.+)$/m)?.[1]?.replace(/[`*_]/g, "") || path.basename(filePath, ".md");
+function typeLabel(schema = {}) {
+ if (schema.oneOf) return schema.oneOf.map(typeLabel).join(" or ");
+ if (schema.type === "array") return `${typeLabel(schema.items || {})}[]`;
+ return schema.type || "any";
}
-async function buildManifest() {
- const documents = [
- {
- id: "readme-zh",
- path: "README_ZH.md",
- sourcePath: "README_ZH.md",
- title: "ReMe 项目介绍",
- description: "核心理念、快速开始、使用场景与社区入口。",
- group: "overview",
- language: "zh",
- },
- {
- id: "readme-en",
- path: "README.md",
- sourcePath: "README.md",
- title: "Introducing ReMe",
- description: "Core ideas, quick start, use cases, and community resources.",
- group: "overview",
- language: "en",
- },
- ];
+function markdownCell(value) {
+ if (value === undefined) return "—";
+ const rendered = typeof value === "string" ? value : JSON.stringify(value);
+ return rendered
+ .replaceAll("<", "<")
+ .replaceAll(">", ">")
+ .replaceAll("|", "\\|")
+ .replaceAll("\n", " ");
+}
- for (const language of ["zh", "en"]) {
- for (const topic of topicOrder) {
- const sourcePath = `docs/${language}/${topic}.md`;
- documents.push({
- id: `${language}-${topic}`,
- path: sourcePath,
- sourcePath,
- title: localizedTitles[topic]?.[language] || (await markdownTitle(path.join(repoDir, sourcePath))),
- description: "",
- group: groups[topic],
- language,
- });
- }
+function buildJobReference(config, language) {
+ const isZh = language === "zh";
+ const jobs = Object.entries(config.jobs || {}).filter(([, job]) => !["background", "cron"].includes(job.backend));
+ const sections = new Map();
- for (const product of productDocuments) {
- const filename = language === "zh" ? "README_ZH.md" : "README.md";
- documents.push({
- id: `${product.slug}-${language}`,
- path: `${product.source}/${filename}`,
- sourcePath: `${product.source}/${filename}`,
- title: product.titles[language],
- description: product.descriptions[language],
- group: product.group,
- language,
- });
- }
+ for (const [name, job] of jobs) {
+ const group = jobGroups[name] || "system";
+ if (!sections.has(group)) sections.set(group, []);
+ sections.get(group).push([name, job]);
}
- return [...documents, ...sharedDocuments].sort(
- (left, right) => navigationGroupOrder.indexOf(left.group) - navigationGroupOrder.indexOf(right.group),
- );
+ const lines = [
+ "---",
+ `title: ${isZh ? "Job API 参考" : "Job API Reference"}`,
+ `description: ${isZh ? "从默认配置自动生成的可调用 Job、参数和服务边界。" : "Callable jobs, parameters, and service boundaries generated from the default configuration."}`,
+ "---",
+ "",
+ `# ${isZh ? "Job API 参考" : "Job API Reference"}`,
+ "",
+ isZh
+ ? "本页从 `reme/config/default.yaml` 自动生成。它描述默认应用中的可调用 Job;插件和自定义配置可以增加、删除或覆盖 Job。运行 `reme help` 可查看当前服务的实际能力。"
+ : "This page is generated from `reme/config/default.yaml`. It describes callable jobs in the default application; plugins and custom configurations may add, remove, or override jobs. Run `reme help` to inspect the active service.",
+ "",
+ isZh
+ ? "> 后台 Job 和 Cron Job 不通过服务暴露,因此不列入调用参考。"
+ : "> Background and cron jobs are not service-exposed and are omitted from the callable reference.",
+ "",
+ ];
+
+ for (const [group, entries] of sections) {
+ lines.push(`## ${groupNames[language][group]}`, "");
+ for (const [name, job] of entries) {
+ const properties = job.parameters?.properties || {};
+ const required = new Set(job.parameters?.required || []);
+ lines.push(`### \`${name}\``, "", markdownCell(job.description || ""), "");
+ lines.push("```bash", `reme ${name}${Object.keys(properties).length ? " ..." : ""}`, "```", "");
+ if (!Object.keys(properties).length) {
+ lines.push(isZh ? "无参数。" : "No parameters.", "");
+ continue;
+ }
+ lines.push(
+ isZh
+ ? "| 参数 | 类型 | 必填 | 默认值 | 说明 |"
+ : "| Parameter | Type | Required | Default | Description |",
+ "|---|---|---:|---|---|",
+ );
+ for (const [parameter, schema] of Object.entries(properties)) {
+ lines.push(
+ `| \`${parameter}\` | \`${markdownCell(typeLabel(schema))}\` | ${required.has(parameter) ? (isZh ? "是" : "yes") : (isZh ? "否" : "no")} | ${markdownCell(schema.default)} | ${markdownCell(schema.description || "—")} |`,
+ );
+ }
+ lines.push("");
+ }
+ }
+ return `${lines.join("\n")}\n`;
}
await rm(path.join(siteDir, ".generated"), { recursive: true, force: true });
await mkdir(outputDir, { recursive: true });
-await cp(path.join(siteDir, "public", "favicon.svg"), path.join(siteDir, ".generated", "favicon.svg"));
-await cp(path.join(siteDir, "public", "CNAME"), path.join(siteDir, ".generated", "CNAME"));
-
-for (const file of ["README.md", "README_ZH.md", "AGENTS.md"]) {
- await cp(path.join(repoDir, file), path.join(outputDir, file));
-}
-await cp(path.join(repoDir, "docs"), path.join(outputDir, "docs"), {
+await cp(path.join(repoDir, "docs"), outputDir, {
recursive: true,
- filter: (source) => path.basename(source) !== ".DS_Store",
+ filter: (source) => ![".DS_Store", "plans"].includes(path.basename(source)),
});
-for (const product of productDocuments) {
- await mkdir(path.join(outputDir, product.source), { recursive: true });
- for (const filename of ["README.md", "README_ZH.md"]) {
- await cp(path.join(repoDir, product.source, filename), path.join(outputDir, product.source, filename));
- }
+await cp(path.join(siteDir, "public", "CNAME"), path.join(outputDir, "public", "CNAME"));
+await cp(path.join(repoDir, "docs/figure/reme-icon.svg"), path.join(outputDir, "public", "reme-icon.svg"));
+await cp(path.join(repoDir, "docs/figure/reme-logo-fashion.svg"), path.join(outputDir, "public", "reme-logo.svg"));
+
+const sourceMap = {};
+for (const [destination, source] of externalDocuments) {
+ const destinationPath = path.join(outputDir, destination);
+ await mkdir(path.dirname(destinationPath), { recursive: true });
+ let content = await readFile(path.join(repoDir, source), "utf8");
+ for (const [from, to] of externalDocumentRewrites[source] || []) content = content.replaceAll(from, to);
+ await writeFile(destinationPath, content);
+ sourceMap[destination] = source;
}
-await writeFile(
- path.join(outputDir, "manifest.json"),
- `${JSON.stringify({ documents: await buildManifest() }, null, 2)}\n`,
-);
+
+await cp(path.join(repoDir, "typescript/figures/dsh"), path.join(outputDir, "public/figures/dsh"), {
+ recursive: true,
+});
+
+for (const language of ["zh", "en"]) {
+ await cp(
+ path.join(repoDir, "benchmark/toolmemory/gitcha.png"),
+ path.join(outputDir, language, "benchmarks", "gitcha.png"),
+ );
+}
+
+const defaultConfig = parseYaml(await readFile(path.join(repoDir, "reme/config/default.yaml"), "utf8"));
+for (const language of ["zh", "en"]) {
+ const destination = path.join(outputDir, language, "reference", "jobs.md");
+ await mkdir(path.dirname(destination), { recursive: true });
+ await writeFile(destination, buildJobReference(defaultConfig, language));
+ sourceMap[`${language}/reference/jobs.md`] = "reme/config/default.yaml";
+}
+
+await writeFile(path.join(outputDir, ".source-map.json"), `${JSON.stringify(sourceMap, null, 2)}\n`);
diff --git a/github-pages/scripts/verify-build.mjs b/github-pages/scripts/verify-build.mjs
new file mode 100644
index 00000000..a6e89e5c
--- /dev/null
+++ b/github-pages/scripts/verify-build.mjs
@@ -0,0 +1,92 @@
+import assert from "node:assert/strict";
+import { access, readFile, readdir } from "node:fs/promises";
+import path from "node:path";
+import { fileURLToPath } from "node:url";
+
+const siteDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
+const outputDir = path.join(siteDir, "dist");
+
+async function collectFiles(directory, prefix = "") {
+ const files = [];
+ for (const entry of await readdir(directory, { withFileTypes: true })) {
+ const relativePath = path.posix.join(prefix, entry.name);
+ if (entry.isDirectory()) files.push(...await collectFiles(path.join(directory, entry.name), relativePath));
+ else files.push(relativePath);
+ }
+ return files;
+}
+
+function pageUrl(relativePath) {
+ if (relativePath === "index.html") return "/";
+ if (relativePath.endsWith("/index.html")) return `/${relativePath.slice(0, -"index.html".length)}`;
+ return `/${relativePath.slice(0, -".html".length)}`;
+}
+
+function routeExists(pathname, files) {
+ const relativePath = decodeURIComponent(pathname).replace(/^\/+/, "");
+ if (!relativePath) return files.has("index.html");
+ if (relativePath.endsWith("/")) return files.has(`${relativePath}index.html`);
+ return files.has(relativePath) || files.has(`${relativePath}.html`) || files.has(`${relativePath}/index.html`);
+}
+
+const requiredFiles = [
+ "index.html",
+ "404.html",
+ "CNAME",
+ "reme-icon.svg",
+ "reme-logo.svg",
+ "hashmap.json",
+ "sitemap.xml",
+ "llms.txt",
+ "llms-full.txt",
+ "zh/index.html",
+ "en/index.html",
+ "zh/configuration.html",
+ "en/configuration.html",
+ "zh/services.html",
+ "en/services.html",
+ "zh/reference/jobs.html",
+ "en/reference/jobs.html",
+ "zh/configuration/llms.txt",
+ "en/configuration/llms.txt",
+];
+
+for (const relativePath of requiredFiles) await access(path.join(outputDir, relativePath));
+
+assert.equal((await readFile(path.join(outputDir, "CNAME"), "utf8")).trim(), "reme.agentscope.io");
+
+const homepage = await readFile(path.join(outputDir, "index.html"), "utf8");
+assert.ok(homepage.includes('href="/en/"'), "root language switch must link to /en/");
+assert.ok(!homepage.includes('href="/en/ex"'), "root language switch must not produce /en/ex");
+assert.ok(homepage.includes('"studio-en":"/en/workspace/studio"'), "legacy redirects must be embedded");
+
+const sitemap = await readFile(path.join(outputDir, "sitemap.xml"), "utf8");
+assert.ok(sitemap.includes(""), "sitemap must include canonical-source update times");
+assert.ok(!sitemap.includes("https://reme.agentscope.io/"), "root redirect must not be indexed");
+assert.ok(!sitemap.includes('hreflang="zh-CN" href="https://reme.agentscope.io/"'), "root must not duplicate zh-CN");
+
+const ChineseConfiguration = await readFile(path.join(outputDir, "zh/configuration.html"), "utf8");
+assert.match(ChineseConfiguration, /搜索文档/);
+assert.match(ChineseConfiguration, /复制 Markdown/);
+assert.match(ChineseConfiguration, /在 GitHub 查看源文件/);
+
+const jobReference = await readFile(path.join(outputDir, "en/reference/jobs.html"), "utf8");
+assert.match(jobReference, /Job API Reference/);
+assert.match(jobReference, /auto_memory/);
+
+const outputFiles = new Set(await collectFiles(outputDir));
+const missingLinks = [];
+for (const relativePath of [...outputFiles].filter((file) => file.endsWith(".html"))) {
+ const html = await readFile(path.join(outputDir, relativePath), "utf8");
+ const currentUrl = new URL(pageUrl(relativePath), "https://reme-docs.local");
+ for (const match of html.matchAll(/]*\bhref="([^"]+)"/g)) {
+ const href = match[1].replaceAll("&", "&");
+ if (href.startsWith("#")) continue;
+ const target = new URL(href, currentUrl);
+ if (target.origin !== currentUrl.origin) continue;
+ if (!routeExists(target.pathname, outputFiles)) missingLinks.push(`${relativePath}: ${href}`);
+ }
+}
+assert.deepEqual(missingLinks, [], `missing internal links:\n${missingLinks.join("\n")}`);
+
+console.log(`Verified ${requiredFiles.length} documentation build artifacts.`);
diff --git a/github-pages/src/main.js b/github-pages/src/main.js
deleted file mode 100644
index 9397ce41..00000000
--- a/github-pages/src/main.js
+++ /dev/null
@@ -1,458 +0,0 @@
-import DOMPurify from "dompurify";
-import { marked } from "marked";
-import { stripMarkdownFrontmatter } from "./markdown.js";
-import "./styles.css";
-
-const baseUrl = import.meta.env.BASE_URL;
-const repositoryUrl = "https://github.com/agentscope-ai/ReMe";
-
-const copy = {
- zh: {
- docs: "文档",
- home: "首页",
- search: "搜索文档…",
- noResults: "没有找到匹配的文档",
- menu: "打开导航",
- toc: "本页目录",
- edit: "在 GitHub 查看源文件",
- quickStart: "快速开始",
- groups: {
- overview: "项目介绍",
- start: "开始使用",
- fundamentals: "核心原理",
- automation: "自动化能力",
- concepts: "架构与场景",
- integration: "Agent 集成",
- workspace: "工作区",
- plugins: "插件",
- benchmarks: "评测",
- development: "开发者规范",
- },
- },
- en: {
- docs: "Documentation",
- home: "Home",
- search: "Search documentation…",
- noResults: "No matching documents",
- menu: "Open navigation",
- toc: "On this page",
- edit: "View source on GitHub",
- quickStart: "Quick start",
- groups: {
- overview: "Introduction",
- start: "Get started",
- fundamentals: "Fundamentals",
- automation: "Automation",
- concepts: "Architecture & scenarios",
- integration: "Agent integration",
- workspace: "Workspace",
- plugins: "Plugins",
- benchmarks: "Benchmarks",
- development: "Development",
- },
- },
-};
-
-const state = {
- language: localStorage.getItem("reme-docs-language") || "zh",
- documents: [],
- activeDocument: null,
- query: "",
-};
-
-const homeCopy = {
- zh: {
- eyebrow: "LOCAL-FIRST · FILE-NATIVE",
- title: "让 Agent 真正记住,\n也让记忆始终属于你。",
- description: "ReMe 将对话和资料沉淀为可读、可编辑、可检索、相互链接的 Markdown,并提供从工作区管理到主动研究的一整套工具。",
- start: "快速开始",
- project: "了解 ReMe",
- explore: "按目标探索",
- exploreDescription: "选择你现在想完成的事情。每个入口都直接连接到对应的完整文档。",
- cards: [
- { id: "studio-zh", icon: "◫", label: "管理记忆", title: "ReMe 工作台", description: "在本地 Web 工作区中浏览、编辑、搜索记忆,并探索 wikilink 图谱。", tone: "mint" },
- { id: "daily-paper-zh", icon: "◌", label: "发现与分析", title: "每日论文插件", description: "从论文榜单筛选值得阅读的工作,解析 PDF,并生成笔记与五分钟简报。", tone: "blue" },
- { id: "auto-fin-zh", icon: "↗", label: "主题研究", title: "Auto Fin 插件", description: "连接最新财联社新闻和本地历史记忆,生成可追溯的研究报告。", tone: "amber" },
- ],
- benchmark: "验证记忆能力",
- benchmarkDescription: "从检索规模、跨会话问答、个人智能体到工具经验,查看 ReMe 的四套评测与复现实验。",
- benchmarkAction: "从 BEAM 开始",
- },
- en: {
- eyebrow: "LOCAL-FIRST · FILE-NATIVE",
- title: "Memory that works for agents.\nFiles that remain yours.",
- description: "ReMe turns conversations and resources into readable, editable, searchable, interconnected Markdown—with tools spanning workspace management and proactive research.",
- start: "Quick start",
- project: "Meet ReMe",
- explore: "Explore by goal",
- exploreDescription: "Start with what you want to accomplish. Every entry opens the complete guide.",
- cards: [
- { id: "studio-en", icon: "◫", label: "Manage memory", title: "ReMe Studio", description: "Browse, edit, and search memory in a local web workspace, then explore its wikilink graph.", tone: "mint" },
- { id: "daily-paper-en", icon: "◌", label: "Discover & analyze", title: "Daily Paper Plugin", description: "Select useful papers from rankings, analyze PDFs, and create notes plus a five-minute brief.", tone: "blue" },
- { id: "auto-fin-en", icon: "↗", label: "Research topics", title: "Auto Fin Plugin", description: "Connect recent CLS news with historical local memory to produce traceable research reports.", tone: "amber" },
- ],
- benchmark: "Validate memory systems",
- benchmarkDescription: "Explore four reproducible evaluations covering retrieval scale, cross-session QA, personal agents, and tool-use experience.",
- benchmarkAction: "Start with BEAM",
- },
-};
-
-const app = document.querySelector("#app");
-
-app.innerHTML = `
-
-
-
-`;
-
-const sidebar = app.querySelector(".sidebar");
-const docsShell = app.querySelector(".docs-shell");
-const documentNav = app.querySelector(".document-nav");
-const article = app.querySelector(".article");
-const toc = app.querySelector(".toc");
-const searchInput = app.querySelector("input[type='search']");
-const menuButton = app.querySelector(".menu-button");
-const backdrop = app.querySelector(".sidebar-backdrop");
-
-function slugify(value) {
- return value
- .toLowerCase()
- .trim()
- .replace(/<[^>]+>/g, "")
- .replace(/[^\p{Letter}\p{Number}]+/gu, "-")
- .replace(/^-|-$/g, "");
-}
-
-function resolveDocumentPath(currentPath, target) {
- const cleanTarget = target.split("#")[0].split("?")[0];
- const currentParts = currentPath.split("/");
- currentParts.pop();
- for (const part of cleanTarget.split("/")) {
- if (!part || part === ".") continue;
- if (part === "..") currentParts.pop();
- else currentParts.push(part);
- }
- return currentParts.join("/");
-}
-
-function configureMarkdown(document) {
- const renderer = new marked.Renderer();
- const headingIds = new Map();
-
- renderer.heading = ({ tokens, depth }) => {
- const text = tokens.map((token) => token.text || token.raw || "").join("");
- const baseSlug = slugify(text) || "section";
- const count = headingIds.get(baseSlug) || 0;
- headingIds.set(baseSlug, count + 1);
- const id = count ? `${baseSlug}-${count + 1}` : baseSlug;
- return `${text}`;
- };
-
- renderer.image = ({ href, title, text }) => {
- const url = /^(https?:|data:)/.test(href)
- ? href
- : `${baseUrl}content/${resolveDocumentPath(document.path, href)}`;
- const titleAttribute = title ? ` title="${title}"` : "";
- return `
`;
- };
-
- renderer.link = ({ href, title, tokens }) => {
- const label = tokens.map((token) => token.text || token.raw || "").join("");
- const titleAttribute = title ? ` title="${title}"` : "";
- if (href.startsWith("#")) return `${label}`;
- if (!/^(https?:|mailto:)/.test(href)) {
- const resolved = resolveDocumentPath(document.path, href);
- const localDocument = state.documents.find((item) => item.path === resolved);
- if (localDocument) return `${label}`;
- return `${label}`;
- }
- return `${label}`;
- };
-
- marked.use({ renderer, gfm: true, breaks: false });
-}
-
-function availableDocuments() {
- return state.documents.filter(
- (document) => document.language === state.language || document.language === "shared",
- );
-}
-
-function documentTitle(document) {
- return document.titles?.[state.language] || document.title;
-}
-
-function renderChrome() {
- const labels = copy[state.language];
- app.querySelector("[data-copy='docs']").textContent = labels.docs;
- const quickStartLink = app.querySelector("[data-copy='quickStart']");
- quickStartLink.textContent = `${labels.quickStart} →`;
- quickStartLink.href = `?doc=${state.language}-quick_start`;
- quickStartLink.dataset.doc = `${state.language}-quick_start`;
- searchInput.placeholder = labels.search;
- menuButton.textContent = labels.menu;
- document.documentElement.lang = state.language === "zh" ? "zh-CN" : "en";
- app.querySelectorAll("[data-language]").forEach((button) => {
- button.classList.toggle("active", button.dataset.language === state.language);
- });
-}
-
-function renderNavigation() {
- const labels = copy[state.language];
- const query = state.query.trim().toLocaleLowerCase();
- const filtered = availableDocuments().filter((document) =>
- `${documentTitle(document)} ${document.title || ""} ${document.description}`.toLocaleLowerCase().includes(query),
- );
- const groups = [...new Set(filtered.map((document) => document.group))];
-
- if (!filtered.length) {
- documentNav.innerHTML = `
- ${labels.home}
- ${labels.noResults}
- `;
- return;
- }
-
- documentNav.innerHTML = `
- ${labels.home}
- ` + groups
- .map(
- (group) => `
-
- ${labels.groups[group]}
- ${filtered
- .filter((document) => document.group === group)
- .map(
- (document) => `
-
- ${documentTitle(document)}
- `,
- )
- .join("")}
- `,
- )
- .join("");
-}
-
-function renderHome(pushHistory = true) {
- const labels = homeCopy[state.language];
- state.activeDocument = null;
- docsShell.classList.add("home-view");
- article.dataset.group = "home";
- renderNavigation();
- article.innerHTML = `
-
- ${labels.eyebrow}
- ${labels.title.replace("\n", "
")}
- ${labels.description}
-
-
-
- 01 / PRODUCT & PLUGINS
- ${labels.explore}
- ${labels.exploreDescription}
-
-
-
- `;
- toc.innerHTML = "";
- closeMenu();
- if (pushHistory) history.pushState({ home: true }, "", baseUrl);
- window.scrollTo({ top: 0, behavior: "instant" });
-}
-
-function renderToc() {
- const headings = [...article.querySelectorAll("h2, h3")];
- if (!headings.length) {
- toc.innerHTML = "";
- return;
- }
- toc.innerHTML = `
- ${copy[state.language].toc}
- ${headings
- .map(
- (heading) => `${heading.childNodes[0]?.textContent || heading.textContent}`,
- )
- .join("")}
- `;
-}
-
-function rewriteRenderedUrls(document) {
- article.querySelectorAll("img[src]").forEach((image) => {
- const source = image.getAttribute("src");
- if (source && !/^(https?:|data:|\/)/.test(source)) {
- image.src = `${baseUrl}content/${resolveDocumentPath(document.path, source)}`;
- }
- });
-
- article.querySelectorAll("a[href]").forEach((link) => {
- const href = link.getAttribute("href");
- if (!href || /^(https?:|mailto:|#|\/)/.test(href) || link.dataset.doc) return;
- const resolved = resolveDocumentPath(document.path, href);
- const localDocument = state.documents.find((item) => item.path === resolved);
- if (localDocument) {
- link.href = `?doc=${localDocument.id}`;
- link.dataset.doc = localDocument.id;
- link.removeAttribute("target");
- return;
- }
- link.href = `${repositoryUrl}/blob/main/${resolved}`;
- link.target = "_blank";
- link.rel = "noreferrer";
- });
-}
-
-async function openDocument(id, pushHistory = true) {
- const fallbackId = state.language === "zh" ? "readme-zh" : "readme-en";
- const document = state.documents.find((item) => item.id === id) || state.documents.find((item) => item.id === fallbackId);
- if (document.language !== "shared" && document.language !== state.language) {
- state.language = document.language;
- localStorage.setItem("reme-docs-language", state.language);
- renderChrome();
- }
- state.activeDocument = document;
- docsShell.classList.remove("home-view");
- article.dataset.group = document.group;
- renderNavigation();
- article.innerHTML = ``;
-
- const response = await fetch(`${baseUrl}content/${document.path}`);
- if (!response.ok) throw new Error(`Unable to load ${document.path}`);
- configureMarkdown(document);
- const markdown = stripMarkdownFrontmatter(await response.text());
- const body = DOMPurify.sanitize(await marked.parse(markdown), {
- ADD_ATTR: ["target"],
- });
- article.innerHTML = `
-
- ${copy[state.language].groups[document.group]}
- ·
- ${document.sourcePath}
-
- ${body}
-
- `;
- rewriteRenderedUrls(document);
- renderToc();
- closeMenu();
- if (pushHistory) history.pushState({ doc: document.id }, "", `?doc=${document.id}`);
- window.scrollTo({ top: 0, behavior: "instant" });
-}
-
-function closeMenu() {
- sidebar.classList.remove("open");
- backdrop.classList.remove("visible");
- menuButton.setAttribute("aria-expanded", "false");
-}
-
-function toggleMenu() {
- const open = !sidebar.classList.contains("open");
- sidebar.classList.toggle("open", open);
- backdrop.classList.toggle("visible", open);
- menuButton.setAttribute("aria-expanded", String(open));
-}
-
-app.addEventListener("click", (event) => {
- const homeLink = event.target.closest("[data-home]");
- if (homeLink) {
- event.preventDefault();
- renderHome();
- return;
- }
- const documentLink = event.target.closest("[data-doc]");
- if (documentLink) {
- event.preventDefault();
- openDocument(documentLink.dataset.doc);
- }
- if (event.target.closest("[data-action='menu']")) toggleMenu();
- const languageButton = event.target.closest("[data-language]");
- if (languageButton && languageButton.dataset.language !== state.language) {
- state.language = languageButton.dataset.language;
- localStorage.setItem("reme-docs-language", state.language);
- state.query = "";
- searchInput.value = "";
- renderChrome();
- renderHome();
- }
-});
-
-searchInput.addEventListener("input", () => {
- state.query = searchInput.value;
- renderNavigation();
-});
-
-document.addEventListener("keydown", (event) => {
- if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
- event.preventDefault();
- searchInput.focus();
- }
- if (event.key === "Escape") closeMenu();
-});
-
-backdrop.addEventListener("click", closeMenu);
-window.addEventListener("popstate", (event) => {
- const id = event.state?.doc || new URLSearchParams(location.search).get("doc");
- if (id) openDocument(id, false);
- else renderHome(false);
-});
-
-const manifest = await fetch(`${baseUrl}content/manifest.json`).then((response) => response.json());
-state.documents = manifest.documents;
-renderChrome();
-const initialDocument = new URLSearchParams(location.search).get("doc");
-if (initialDocument) await openDocument(initialDocument, false);
-else renderHome(false);
diff --git a/github-pages/src/markdown.js b/github-pages/src/markdown.js
deleted file mode 100644
index ef81a773..00000000
--- a/github-pages/src/markdown.js
+++ /dev/null
@@ -1,6 +0,0 @@
-const FRONTMATTER_PATTERN = /^\uFEFF?---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/;
-
-/** Remove a leading YAML frontmatter block before rendering Markdown. */
-export function stripMarkdownFrontmatter(markdown) {
- return markdown.replace(FRONTMATTER_PATTERN, "");
-}
diff --git a/github-pages/src/styles.css b/github-pages/src/styles.css
deleted file mode 100644
index 7b3ee4ad..00000000
--- a/github-pages/src/styles.css
+++ /dev/null
@@ -1,200 +0,0 @@
-:root {
- color: #17221d;
- background: #f4f7f5;
- font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
- font-synthesis: none;
- text-rendering: optimizeLegibility;
- --ink: #17221d;
- --muted: #65716a;
- --line: #dce5e0;
- --paper: #ffffff;
- --green: #087f6a;
- --blue: #3156d9;
- --green-soft: #e5f5ef;
- --code: #f3f5f2;
-}
-
-* { box-sizing: border-box; }
-html { scroll-behavior: smooth; scroll-padding-top: 92px; }
-body {
- margin: 0;
- min-width: 320px;
- background:
- radial-gradient(circle at 8% 9%, rgba(27, 193, 164, 0.06), transparent 26rem),
- #f4f7f5;
-}
-button, input { font: inherit; }
-a { color: inherit; }
-
-.topbar {
- position: fixed;
- inset: 0 0 auto 0;
- z-index: 30;
- height: 66px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 0 28px;
- border-bottom: 1px solid rgba(213, 225, 219, 0.85);
- background: rgba(250, 252, 251, 0.88);
- backdrop-filter: blur(18px) saturate(140%);
-}
-
-.brand { display: flex; align-items: center; gap: 11px; color: var(--ink); text-decoration: none; font-weight: 760; }
-.brand-mark {
- display: grid;
- place-items: center;
- width: 34px;
- height: 34px;
- border-radius: 11px;
- color: white;
- background: linear-gradient(145deg, #19c9b0, #3156d9 82%);
- box-shadow: 0 7px 18px rgba(24, 123, 114, 0.2);
- font-family: Georgia, serif;
- font-size: 21px;
-}
-.brand-divider { width: 1px; height: 20px; background: var(--line); margin-left: 2px; }
-.brand-section { color: var(--muted); font-weight: 520; }
-.top-actions { display: flex; align-items: center; gap: 18px; }
-.github-link, .quick-start-link { color: #37443d; text-decoration: none; font-size: 13px; font-weight: 650; }
-.github-link:hover, .quick-start-link:hover { color: var(--green); }
-.quick-start-link { padding: 8px 12px; border: 1px solid #d7e5df; border-radius: 9px; background: rgba(255, 255, 255, 0.72); }
-.language-switch { display: flex; padding: 3px; border: 1px solid var(--line); border-radius: 9px; background: #f5f7f4; }
-.language-switch button { padding: 5px 9px; border: 0; border-radius: 6px; color: var(--muted); background: transparent; cursor: pointer; font-size: 12px; font-weight: 700; }
-.language-switch button.active { color: var(--ink); background: white; box-shadow: 0 1px 3px rgba(20, 40, 30, 0.1); }
-.menu-button { display: none; border: 1px solid var(--line); border-radius: 8px; background: white; padding: 7px 10px; cursor: pointer; }
-
-.docs-shell { display: grid; grid-template-columns: 276px minmax(0, 1fr) 224px; max-width: 1540px; min-height: 100vh; margin: 0 auto; padding-top: 66px; }
-.docs-shell.home-view { grid-template-columns: 276px minmax(0, 1fr); }
-.docs-shell.home-view .toc-panel { display: none; }
-.sidebar { position: sticky; top: 66px; height: calc(100vh - 66px); padding: 25px 20px 18px; overflow-y: auto; border-right: 1px solid var(--line); background: rgba(247, 250, 248, 0.78); }
-.search-box { display: flex; align-items: center; gap: 8px; height: 41px; padding: 0 11px; border: 1px solid #d8e3dd; border-radius: 11px; color: #7a857e; background: rgba(255, 255, 255, 0.84); box-shadow: 0 5px 18px rgba(29, 65, 48, 0.035); }
-.search-box:focus-within { border-color: #78aa8e; box-shadow: 0 0 0 3px rgba(22, 120, 76, 0.1); }
-.search-box input { width: 100%; border: 0; outline: 0; color: var(--ink); background: transparent; font-size: 13px; }
-.search-box kbd { padding: 2px 5px; border: 1px solid var(--line); border-radius: 4px; background: #f7f8f6; font-size: 10px; }
-.document-nav { padding: 12px 0 52px; }
-.home-link { position: relative; display: block; margin-top: 9px; padding: 9px 10px 9px 13px; border-radius: 8px; color: #536058; text-decoration: none; font-size: 13px; }
-.home-link:hover { color: var(--ink); background: #f0f3ef; }
-.home-link.active { color: #086b5a; background: linear-gradient(90deg, #dff3ec, #eaf6f2); font-weight: 700; }
-.nav-group { margin-top: 21px; }
-.nav-group h2 { margin: 0 10px 7px; color: #8a948e; font-size: 10px; font-weight: 800; letter-spacing: 0.11em; text-transform: uppercase; }
-.nav-group a { position: relative; display: block; padding: 7px 10px 7px 13px; border-radius: 8px; color: #536058; text-decoration: none; font-size: 13px; line-height: 1.4; }
-.nav-group a:hover { color: var(--ink); background: #f0f3ef; }
-.nav-group a.active { color: #086b5a; background: linear-gradient(90deg, #dff3ec, #eaf6f2); font-weight: 700; }
-.nav-group a.active::before { position: absolute; top: 9px; bottom: 9px; left: 0; width: 3px; border-radius: 3px; background: linear-gradient(#17b79d, #3470d8); content: ""; }
-.empty-state { padding: 24px 10px; color: var(--muted); font-size: 13px; }
-.sidebar-footer { position: sticky; bottom: -18px; display: flex; align-items: center; gap: 8px; margin: 0 -20px; padding: 14px 22px 18px; border-top: 1px solid var(--line); color: #7c8880; background: #f7faf8; font: 600 10px/1.2 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0.04em; text-transform: uppercase; }
-.status-dot { width: 6px; height: 6px; border-radius: 50%; background: #29a869; box-shadow: 0 0 0 3px #dff3e8; }
-
-.article-wrap { min-width: 0; padding: 58px clamp(32px, 5.8vw, 86px) 100px; background: rgba(255, 255, 255, 0.94); }
-.article { width: 100%; max-width: 820px; margin: 0 auto; }
-.article[data-group="home"] { max-width: 980px; }
-.home-hero { padding: 34px 0 76px; }
-.home-eyebrow, .section-kicker { margin: 0 0 17px; color: #12806d; font: 750 11px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0.13em; }
-.home-hero h1 { margin: 0; color: #102019; font-size: clamp(46px, 6.3vw, 76px); line-height: 1.15; letter-spacing: -0.055em; }
-.home-lead { margin: 27px 0 0; color: #526159; font-size: 18px; line-height: 1.72; }
-.home-actions { display: flex; flex-wrap: wrap; gap: 11px; margin-top: 31px; }
-.home-actions a { padding: 11px 17px; border-radius: 10px; text-decoration: none; font-size: 14px; font-weight: 720; }
-.primary-action { color: white; background: #087f6a; box-shadow: 0 8px 22px rgba(8, 127, 106, 0.2); }
-.secondary-action { border: 1px solid #d5e2dc; color: #34443c; background: white; }
-.home-explore { padding-top: 58px; border-top: 1px solid var(--line); }
-.home-explore h2, .benchmark-callout h2 { margin: 0; color: #15251d; font-size: 30px; letter-spacing: -0.025em; }
-.section-lead { max-width: 620px; margin: 10px 0 25px; color: var(--muted); line-height: 1.65; }
-.feature-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
-.feature-card { position: relative; display: flex; min-height: 260px; flex-direction: column; padding: 23px; overflow: hidden; border: 1px solid #dce8e2; border-radius: 16px; color: var(--ink); background: linear-gradient(155deg, #fff, #f4faf7); text-decoration: none; transition: transform 160ms ease, box-shadow 160ms ease; }
-.feature-card:hover { transform: translateY(-3px); box-shadow: 0 16px 34px rgba(29, 68, 49, 0.1); }
-.feature-card.blue { background: linear-gradient(155deg, #fff, #f1f5ff); }
-.feature-card.amber { background: linear-gradient(155deg, #fff, #fbf7ec); }
-.feature-icon { display: grid; place-items: center; width: 42px; height: 42px; margin-bottom: 32px; border-radius: 12px; color: #08705e; background: #dbf2ea; font-size: 22px; }
-.blue .feature-icon { color: #3156b8; background: #e5ebff; }
-.amber .feature-icon { color: #9b6818; background: #f8eac8; }
-.feature-label { margin-bottom: 7px; color: #718078; font: 700 10px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0.08em; text-transform: uppercase; }
-.feature-card strong { font-size: 20px; }
-.feature-description { margin-top: 10px; color: #66736c; font-size: 13px; line-height: 1.6; }
-.feature-arrow { position: absolute; right: 22px; bottom: 18px; color: #6a7870; font-size: 19px; }
-.benchmark-callout { display: flex; align-items: end; justify-content: space-between; gap: 35px; margin-top: 62px; padding: 36px; border-radius: 17px; color: white; background: linear-gradient(125deg, #142a22, #1c473b); }
-.benchmark-callout .section-kicker { color: #74d4bb; }
-.benchmark-callout h2 { color: white; }
-.benchmark-callout p:not(.section-kicker) { max-width: 610px; margin: 10px 0 0; color: #c3d4cd; line-height: 1.65; }
-.benchmark-callout > a { flex: none; padding: 10px 14px; border: 1px solid rgba(255,255,255,.25); border-radius: 9px; color: white; text-decoration: none; font-size: 13px; font-weight: 700; }
-.article-meta { display: flex; gap: 8px; margin-bottom: 22px; color: #7d8d84; font: 600 11px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; }
-.article-meta span:first-child { padding: 3px 8px; border-radius: 999px; color: #08705e; background: #e6f5ef; }
-.markdown-body { color: #27332d; font-size: 16px; line-height: 1.78; }
-.markdown-body > :first-child { margin-top: 0; }
-.markdown-body h1 { margin: 0 0 30px; color: #112019; font-size: clamp(36px, 5vw, 54px); line-height: 1.06; letter-spacing: -0.04em; }
-.markdown-body h2 { margin: 58px 0 18px; padding-top: 4px; color: #14241c; font-size: 27px; line-height: 1.25; letter-spacing: -0.02em; }
-.markdown-body h3 { margin: 35px 0 12px; color: #1f3027; font-size: 20px; line-height: 1.35; }
-.markdown-body p, .markdown-body ul, .markdown-body ol { margin: 14px 0; }
-.markdown-body li { margin: 5px 0; }
-.markdown-body a { color: var(--green); text-decoration-color: #9cc8ae; text-underline-offset: 3px; }
-.markdown-body a:hover { text-decoration-color: var(--green); }
-.markdown-body img { display: block; max-width: 100%; height: auto; margin: 30px auto; border-radius: 14px; }
-/* README badges are linked images. Keep them in a compact row instead of applying the figure layout above. */
-.article[data-group="overview"] .markdown-body > p:has(> a > img[src*="img.shields.io"]) {
- display: flex;
- flex-wrap: wrap;
- justify-content: center;
- gap: 7px;
- max-width: 690px;
- margin: 20px auto;
-}
-.article[data-group="overview"] .markdown-body > p > a > img[src*="img.shields.io"] {
- display: inline-block;
- width: auto;
- max-height: 24px;
- margin: 0;
- border-radius: 4px;
- vertical-align: middle;
-}
-.markdown-body p[align="center"] { text-align: center; }
-.article[data-group="overview"] .markdown-body > p:first-child img { max-width: min(470px, 72%); margin-top: 8px; filter: drop-shadow(0 18px 25px rgba(44, 85, 164, 0.1)); }
-.markdown-body code { padding: 2px 5px; border-radius: 5px; background: var(--code); color: #315844; font: 0.88em/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
-.markdown-body pre { margin: 24px 0; padding: 19px 21px; overflow-x: auto; border: 1px solid #dce5e0; border-radius: 12px; background: linear-gradient(145deg, #f5f8f6, #f8faf9); box-shadow: inset 3px 0 0 #7bd0b6; }
-.markdown-body pre code { padding: 0; color: #263b30; background: none; font-size: 13px; }
-.markdown-body blockquote { margin: 24px 0; padding: 4px 20px; border-left: 3px solid #68aa85; color: #53635a; background: #f5faf7; }
-.markdown-body table { display: block; width: 100%; margin: 24px 0; overflow-x: auto; border-collapse: collapse; font-size: 14px; }
-.markdown-body th, .markdown-body td { padding: 10px 13px; border: 1px solid var(--line); text-align: left; }
-.markdown-body th { background: #f4f6f3; }
-.markdown-body hr { margin: 44px 0; border: 0; border-top: 1px solid var(--line); }
-.article-footer { margin-top: 70px; padding-top: 20px; border-top: 1px solid var(--line); }
-.article-footer a { color: var(--muted); text-decoration: none; font-size: 13px; }
-.article-footer a:hover { color: var(--green); }
-.loading-line { width: 55%; height: 12px; margin-top: 40px; border-radius: 9px; background: linear-gradient(90deg, #edf0ec, #f8faf7, #edf0ec); background-size: 200% 100%; animation: loading 1.2s infinite; }
-@keyframes loading { to { background-position: -200% 0; } }
-
-.toc-panel { position: sticky; top: 66px; height: calc(100vh - 66px); padding: 58px 24px; overflow-y: auto; border-left: 1px solid #e9efeb; background: rgba(252, 253, 252, 0.92); }
-.toc h2 { margin: 0 0 12px; color: #8a948e; font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; }
-.toc a { display: block; padding: 5px 0; color: #7a857e; text-decoration: none; font-size: 12px; line-height: 1.45; }
-.toc a:hover { color: var(--green); }
-.toc .toc-h3 { padding-left: 12px; }
-.sidebar-backdrop { display: none; }
-
-@media (max-width: 1120px) {
- .docs-shell { grid-template-columns: 250px minmax(0, 1fr); }
- .toc-panel { display: none; }
-}
-
-@media (max-width: 760px) {
- .topbar { height: 60px; padding: 0 16px; }
- .brand-section, .brand-divider, .github-link { display: none; }
- .quick-start-link { font-size: 12px; }
- .menu-button { display: block; max-width: 112px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
- .top-actions { gap: 9px; }
- .docs-shell { display: block; padding-top: 60px; }
- .sidebar { position: fixed; z-index: 25; top: 60px; bottom: 0; left: 0; width: min(310px, 86vw); height: auto; transform: translateX(-105%); transition: transform 180ms ease; box-shadow: 16px 0 35px rgba(20, 40, 30, 0.12); }
- .sidebar.open { transform: translateX(0); }
- .sidebar-backdrop { position: fixed; z-index: 20; inset: 60px 0 0; width: 100%; border: 0; background: rgba(18, 30, 23, 0.35); }
- .sidebar-backdrop.visible { display: block; }
- .article-wrap { padding: 38px 20px 72px; }
- .article-meta { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
- .markdown-body { font-size: 15px; }
- .markdown-body h1 { font-size: 34px; }
- .markdown-body h2 { margin-top: 46px; font-size: 24px; }
- .article[data-group="overview"] .markdown-body > p:first-child img { max-width: 84%; }
- .home-hero { padding: 20px 0 54px; }
- .home-hero h1 { font-size: 43px; }
- .home-lead { font-size: 16px; }
- .feature-grid { grid-template-columns: 1fr; }
- .feature-card { min-height: 225px; }
- .benchmark-callout { align-items: flex-start; flex-direction: column; padding: 28px 24px; }
-}
diff --git a/github-pages/tests/content-manifest.test.mjs b/github-pages/tests/content-manifest.test.mjs
deleted file mode 100644
index bc1a8a0b..00000000
--- a/github-pages/tests/content-manifest.test.mjs
+++ /dev/null
@@ -1,23 +0,0 @@
-import assert from "node:assert/strict";
-import { readFile } from "node:fs/promises";
-import test from "node:test";
-
-const manifestUrl = new URL("../.generated/content/manifest.json", import.meta.url);
-
-test("omits retired Agent documents and places Agent integration after getting started", async () => {
- const manifest = JSON.parse(await readFile(manifestUrl, "utf8"));
- const documents = manifest.documents;
- const groups = [...new Set(documents.map((document) => document.group))];
-
- assert.equal(documents.some((document) => document.id === "reme-memory-skill"), false);
- assert.equal(documents.some((document) => document.sourcePath.endsWith("agent_integration_plan.md")), false);
- assert.deepEqual(
- documents
- .filter((document) => document.group === "plugins")
- .map((document) => document.title || document.titles?.en),
- ["每日论文插件", "Auto Fin 插件", "Daily Paper Plugin", "Auto Fin Plugin"],
- );
- assert.equal(documents.some((document) => document.group === "cookbooks"), false);
- assert.ok(groups.indexOf("integration") > groups.indexOf("start"));
- assert.ok(groups.indexOf("integration") < groups.indexOf("fundamentals"));
-});
diff --git a/github-pages/tests/generated-content.test.mjs b/github-pages/tests/generated-content.test.mjs
new file mode 100644
index 00000000..6aceeaee
--- /dev/null
+++ b/github-pages/tests/generated-content.test.mjs
@@ -0,0 +1,109 @@
+import assert from "node:assert/strict";
+import { access, readFile } from "node:fs/promises";
+import path from "node:path";
+import test from "node:test";
+import { fileURLToPath } from "node:url";
+import { parse as parseYaml } from "yaml";
+import { legacyRoutes } from "../../docs/.vitepress/legacy-routes.mjs";
+
+const siteDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
+const repoDir = path.resolve(siteDir, "..");
+const generatedDir = path.join(siteDir, ".generated", "site");
+
+test("generates every required bilingual guide", async () => {
+ const names = [
+ "configuration.md",
+ "services.md",
+ "operations.md",
+ "integrations.md",
+ "plugin_development.md",
+ "faq.md",
+ "reference/cli.md",
+ "reference/jobs.md",
+ ];
+ for (const language of ["zh", "en"]) {
+ for (const name of names) await access(path.join(generatedDir, language, name));
+ }
+ await access(path.join(generatedDir, "zh/integrations/claude-code.md"));
+ await access(path.join(generatedDir, "en/integrations/claude-code.md"));
+ await access(path.join(generatedDir, "zh/integrations/hermes.md"));
+ await access(path.join(generatedDir, "en/integrations/hermes.md"));
+ await access(path.join(generatedDir, "zh/integrations/dsh.md"));
+ await access(path.join(generatedDir, "en/integrations/dsh.md"));
+ await access(path.join(generatedDir, "zh/integrations/openclaw.md"));
+ await access(path.join(generatedDir, "en/integrations/openclaw.md"));
+ await access(path.join(generatedDir, "public/figures/dsh/reme-status-overview.png"));
+});
+
+test("maps mirrored pages back to their canonical repository sources", async () => {
+ const sourceMap = JSON.parse(await readFile(path.join(generatedDir, ".source-map.json"), "utf8"));
+ assert.equal(sourceMap["zh/integrations/typescript.md"], "typescript/README_ZH.md");
+ assert.equal(sourceMap["en/integrations/dsh.md"], "typescript/docs/dsh.md");
+ assert.equal(sourceMap["zh/integrations/openclaw.md"], "typescript/docs/openclaw.zh-CN.md");
+ assert.equal(sourceMap["en/integrations/claude-code.md"], "integrations/claude_code/README.md");
+ assert.equal(sourceMap["en/integrations/hermes.md"], "integrations/hermes_agent/README.md");
+ assert.equal(sourceMap["en/workspace/studio.md"], "reme_studio/README.md");
+ assert.equal(sourceMap["zh/plugins/lme.md"], "plugins/lme/README_ZH.md");
+ assert.equal(sourceMap["en/reference/jobs.md"], "reme/config/default.yaml");
+});
+
+test("publishes portable and accurate DSH instructions", async () => {
+ const english = await readFile(path.join(generatedDir, "en/integrations/dsh.md"), "utf8");
+ const chinese = await readFile(path.join(generatedDir, "zh/integrations/dsh.md"), "utf8");
+ assert.doesNotMatch(english, /\/Users\//);
+ assert.doesNotMatch(chinese, /\/Users\//);
+ assert.match(english, /runtime counters refresh every 5 seconds/);
+ assert.match(chinese, /每 5 秒仅刷新 DSH 插件的运行时计数/);
+});
+
+test("generates the callable Job reference from default.yaml", async () => {
+ const config = parseYaml(await readFile(path.join(repoDir, "reme/config/default.yaml"), "utf8"));
+ const callableJobs = Object.entries(config.jobs)
+ .filter(([, job]) => !["background", "cron"].includes(job.backend))
+ .map(([name]) => name);
+
+ for (const language of ["zh", "en"]) {
+ const reference = await readFile(path.join(generatedDir, language, "reference", "jobs.md"), "utf8");
+ for (const job of callableJobs) assert.ok(reference.includes(`### ${"`"}${job}${"`"}`), job);
+ }
+});
+
+test("keeps generated content disposable and excludes internal plans", async () => {
+ await assert.rejects(access(path.join(generatedDir, "plans")));
+ await access(path.join(generatedDir, ".vitepress", "config.mts"));
+ await access(path.join(generatedDir, "public", "reme-icon.svg"));
+ await access(path.join(generatedDir, "public", "reme-logo.svg"));
+ assert.equal(
+ (await readFile(path.join(generatedDir, "public", "CNAME"), "utf8")).trim(),
+ "reme.agentscope.io",
+ );
+});
+
+test("maps every legacy query-string document ID to a generated page", async () => {
+ assert.equal(Object.keys(legacyRoutes).length, 45);
+ assert.equal(legacyRoutes["studio-en"], "/en/workspace/studio");
+ assert.equal(legacyRoutes["en-quick_start"], "/en/quick_start");
+ assert.equal(legacyRoutes["agents-guide"], "https://github.com/agentscope-ai/ReMe/blob/main/AGENTS.md");
+
+ for (const [id, route] of Object.entries(legacyRoutes)) {
+ if (route.startsWith("https://")) continue;
+ assert.match(route, /^\/(?:zh|en)\//, id);
+ const relative = route.endsWith("/") ? `${route.slice(1)}index.md` : `${route.slice(1)}.md`;
+ await access(path.join(generatedDir, relative));
+ }
+});
+
+test("tracks every generated input in documentation CI and deployment", async () => {
+ const requiredPaths = [
+ "reme/config/default.yaml",
+ "integrations/claude_code/README.md",
+ "integrations/hermes_agent/README.md",
+ "typescript/docs/**",
+ "typescript/figures/**",
+ "benchmark/toolmemory/gitcha.png",
+ ];
+ for (const workflow of ["ci-docs.yml", "deploy-docs.yml"]) {
+ const source = await readFile(path.join(repoDir, ".github/workflows", workflow), "utf8");
+ for (const requiredPath of requiredPaths) assert.ok(source.includes(requiredPath), `${workflow}: ${requiredPath}`);
+ }
+});
diff --git a/github-pages/tests/markdown.test.mjs b/github-pages/tests/markdown.test.mjs
deleted file mode 100644
index 129b6190..00000000
--- a/github-pages/tests/markdown.test.mjs
+++ /dev/null
@@ -1,15 +0,0 @@
-import assert from "node:assert/strict";
-import test from "node:test";
-import { stripMarkdownFrontmatter } from "../src/markdown.js";
-
-test("strips leading YAML frontmatter before rendering", () => {
- assert.equal(
- stripMarkdownFrontmatter("---\nname: reme_memory\ndescription: Memory skill\n---\n\n# ReMe Memory\n"),
- "\n# ReMe Memory\n",
- );
-});
-
-test("preserves Markdown without frontmatter", () => {
- const markdown = "# ReMe Memory\n\nContent\n";
- assert.equal(stripMarkdownFrontmatter(markdown), markdown);
-});
diff --git a/github-pages/vite.config.js b/github-pages/vite.config.js
deleted file mode 100644
index 1445f872..00000000
--- a/github-pages/vite.config.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import { defineConfig } from "vite";
-
-export default defineConfig({
- base: "./",
- publicDir: ".generated",
- build: {
- outDir: "dist",
- emptyOutDir: true,
- },
-});
diff --git a/plugins/beam/LICENSE b/plugins/beam/LICENSE
new file mode 100644
index 00000000..65c2c5cf
--- /dev/null
+++ b/plugins/beam/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2025 Alibaba Group
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/plugins/beam/README.md b/plugins/beam/README.md
new file mode 100644
index 00000000..fb2f9559
--- /dev/null
+++ b/plugins/beam/README.md
@@ -0,0 +1,38 @@
+# BEAM plugin
+
+[中文说明](./README_ZH.md)
+
+This plugin owns the BEAM memory, agentic-answer and judge Steps, their prompts,
+and their Job defaults in `plugin.yaml`. ReMe's built-in `benchmark.yaml` owns the
+shared evaluation Jobs and components. Dataset handling, the runner and results
+remain in [`benchmark/beam`](../../benchmark/beam/README.md).
+
+From the repository root, install ReMe and this plugin in editable mode before running the benchmark:
+
+```bash
+python -m pip install -e ".[as]"
+reme plugins install ./plugins/beam --editable
+reme plugins validate beam
+python benchmark/beam/run.py
+```
+
+Editable installation registers the `beam` entry point while keeping source changes immediately
+visible. The runner selects the built-in `benchmark` preset and explicitly enables `beam` for
+each Application. Installing the plugin makes it discoverable but does not enable it globally.
+
+`plugin.yaml` registers backends and contributes the plugin-owned `auto_memory`,
+`agentic_answer` and `answer_judge` Job defaults. Start the installed plugin with
+`reme start config=benchmark plugins='["beam"]'`. The shared preset does not inherit
+`default`: only declared Jobs run, indexing is manual, and neither scheduled dream
+nor the optional `auto_dream` Job is enabled.
+The existing `auto_memory`, `agentic_answer`, `answer_judge`, `bench` and `judge`
+names and model environment variables are unchanged. Explicit application/CLI overrides
+still take precedence. Installing this plugin does not start an evaluation.
+
+The shared answer base class lives in `reme.steps.benchmark.base_agentic_answer`.
+The old core-owned `reme.steps.benchmark.beam` Python import path is removed.
+Custom Python callers should import memory, search and answer Steps from `reme_beam`, and the
+judge Step from `judge_beam`. After uninstalling,
+Applications and CLI services must omit the plugin until it is installed again.
+Uninstallation never removes datasets, workspaces or results.
+Restart an existing service after changing plugins.
diff --git a/plugins/beam/README_ZH.md b/plugins/beam/README_ZH.md
new file mode 100644
index 00000000..4d695715
--- /dev/null
+++ b/plugins/beam/README_ZH.md
@@ -0,0 +1,32 @@
+# BEAM 插件
+
+[English](./README.md)
+
+插件包含 BEAM 的记忆、回答、评分 Step、提示词,以及 `plugin.yaml` 中对应的 Job 默认配置。
+ReMe 内置的 `benchmark.yaml` 负责公共评测 Job 和 Component;数据集处理、runner 和结果仍留在
+[`benchmark/beam`](../../benchmark/beam/README_ZH.md)。
+
+在仓库根目录以 editable 模式安装 ReMe 和本插件,再运行评测:
+
+```bash
+python -m pip install -e ".[as]"
+reme plugins install ./plugins/beam --editable
+reme plugins validate beam
+python benchmark/beam/run.py
+```
+
+editable 安装会注册 `beam` entry point,并让源码修改立即生效。runner 选择内置 `benchmark`
+配置,并为每个 Application 显式启用 `beam`。安装只让插件可被发现,不会在所有应用中全局启用。
+
+`plugin.yaml` 注册 backend,并通过 `application_defaults` 提供插件拥有的 `auto_memory`、
+`agentic_answer` 和 `answer_judge` Job。安装后使用
+`reme start config=benchmark plugins='["beam"]'`。公共评测配置不继承 `default`,只运行声明的 Job:
+索引手动更新,dream 定时任务和可选的 `auto_dream`
+均保持关闭。原有 `auto_memory`、`agentic_answer`、`answer_judge`、`bench`、`judge` 名称及模型环境变量
+保持不变,显式应用参数和 CLI 覆盖仍优先。安装或启用插件不会自动开始评测。
+
+共享回答基类位于 `reme.steps.benchmark.base_agentic_answer`。
+原 `reme.steps.benchmark.beam` Python 导入路径已移除。自定义 Python 调用应从 `reme_beam`
+导入记忆、搜索和回答 Step,并从 `judge_beam` 导入评判 Step。
+卸载插件后,Application 和 CLI 服务必须移除插件选择,直到再次安装。
+卸载不会删除数据集、工作区或结果。修改插件后需重启已有服务。
diff --git a/plugins/beam/pyproject.toml b/plugins/beam/pyproject.toml
new file mode 100644
index 00000000..2b35f5aa
--- /dev/null
+++ b/plugins/beam/pyproject.toml
@@ -0,0 +1,28 @@
+[project]
+name = "reme-beam"
+version = "0.1.0"
+description = "BEAM benchmark plugin for ReMe."
+readme = "README.md"
+license = "Apache-2.0"
+license-files = ["LICENSE"]
+requires-python = ">=3.11"
+dependencies = [
+ "reme-ai[as]>=0.4.1.11",
+ "json-repair",
+ "numpy>=2.2.6",
+]
+
+[project.entry-points."reme.plugins"]
+beam = "reme_beam"
+
+[tool.setuptools.packages.find]
+where = ["src"]
+include = ["reme_beam*", "judge_beam*"]
+
+[tool.setuptools.package-data]
+reme_beam = ["plugin.yaml", "*.yaml"]
+judge_beam = ["*.yaml"]
+
+[build-system]
+requires = ["setuptools>=77", "wheel"]
+build-backend = "setuptools.build_meta"
diff --git a/plugins/beam/src/judge_beam/__init__.py b/plugins/beam/src/judge_beam/__init__.py
new file mode 100644
index 00000000..7090e1ce
--- /dev/null
+++ b/plugins/beam/src/judge_beam/__init__.py
@@ -0,0 +1,5 @@
+"""BEAM benchmark judge backend."""
+
+from .llm_judge import BeamRubricJudgeStep
+
+__all__ = ["BeamRubricJudgeStep"]
diff --git a/reme/steps/benchmark/beam/llm_judge.py b/plugins/beam/src/judge_beam/llm_judge.py
similarity index 98%
rename from reme/steps/benchmark/beam/llm_judge.py
rename to plugins/beam/src/judge_beam/llm_judge.py
index 879c82e3..fdadabbb 100644
--- a/reme/steps/benchmark/beam/llm_judge.py
+++ b/plugins/beam/src/judge_beam/llm_judge.py
@@ -24,10 +24,9 @@ from typing import List, Tuple
import numpy as np
from json_repair import repair_json
-from ...base_step import BaseStep, Ref
-from ....components import R
-from ....components.as_embedding import BaseAsEmbedding
-from ....enumeration import ComponentEnum
+from reme.steps.base_step import BaseStep, Ref
+from reme.components.as_embedding import BaseAsEmbedding
+from reme.enumeration import ComponentEnum
# ---------------------------------------------------------------------------
@@ -231,7 +230,6 @@ def _event_ordering_score(
}
-@R.register("beam_rubric_judge_step")
class BeamRubricJudgeStep(BaseStep):
"""Judge an LLM response against a list of rubric criteria.
diff --git a/reme/steps/benchmark/beam/llm_judge.yaml b/plugins/beam/src/judge_beam/llm_judge.yaml
similarity index 100%
rename from reme/steps/benchmark/beam/llm_judge.yaml
rename to plugins/beam/src/judge_beam/llm_judge.yaml
diff --git a/reme/steps/benchmark/beam/__init__.py b/plugins/beam/src/reme_beam/__init__.py
similarity index 56%
rename from reme/steps/benchmark/beam/__init__.py
rename to plugins/beam/src/reme_beam/__init__.py
index e8c6e48f..2a5e8a7d 100644
--- a/reme/steps/benchmark/beam/__init__.py
+++ b/plugins/beam/src/reme_beam/__init__.py
@@ -1,11 +1,11 @@
-"""BEAM benchmark steps."""
+"""BEAM benchmark backends and application configuration for ReMe."""
from .agentic_answer import BeamAgenticAnswerStep
-from .llm_judge import BeamRubricJudgeStep
from .auto_memory import BeamAutoMemoryStep
+from .search_v2 import SearchV2Step
__all__ = [
"BeamAgenticAnswerStep",
- "BeamRubricJudgeStep",
"BeamAutoMemoryStep",
+ "SearchV2Step",
]
diff --git a/reme/steps/benchmark/beam/agentic_answer.py b/plugins/beam/src/reme_beam/agentic_answer.py
similarity index 80%
rename from reme/steps/benchmark/beam/agentic_answer.py
rename to plugins/beam/src/reme_beam/agentic_answer.py
index 3b9a02fc..d6452ec0 100644
--- a/reme/steps/benchmark/beam/agentic_answer.py
+++ b/plugins/beam/src/reme_beam/agentic_answer.py
@@ -1,10 +1,8 @@
"""BEAM agentic answer step – ReAct agent that answers questions using the search tool."""
-from ....components import R
-from ..base import BaseAgenticAnswerStep
+from reme.steps.benchmark import BaseAgenticAnswerStep
-@R.register("beam_agentic_answer_step")
class BeamAgenticAnswerStep(BaseAgenticAnswerStep):
"""Answer a BEAM probing question via ReAct agent with access to the search tool.
diff --git a/reme/steps/benchmark/beam/agentic_answer.yaml b/plugins/beam/src/reme_beam/agentic_answer.yaml
similarity index 100%
rename from reme/steps/benchmark/beam/agentic_answer.yaml
rename to plugins/beam/src/reme_beam/agentic_answer.yaml
diff --git a/reme/steps/benchmark/beam/auto_memory.py b/plugins/beam/src/reme_beam/auto_memory.py
similarity index 98%
rename from reme/steps/benchmark/beam/auto_memory.py
rename to plugins/beam/src/reme_beam/auto_memory.py
index cccb0c28..378f97bf 100644
--- a/reme/steps/benchmark/beam/auto_memory.py
+++ b/plugins/beam/src/reme_beam/auto_memory.py
@@ -4,9 +4,8 @@ from datetime import datetime, timedelta
from agentscope.message import Msg
-from ...evolve.auto_memory import AutoMemoryStep, _normalize_msg_timestamp
-from ...file_io import validate_session_id
-from ....components import R
+from reme.steps.evolve.auto_memory import AutoMemoryStep, _normalize_msg_timestamp
+from reme.steps.file_io import validate_session_id
# Runtime-context key carrying the 0-based line offset of the current segment
# inside the full session file (segmented ingestion of long sessions).
@@ -168,7 +167,6 @@ def _interpolate_timestamps(items: list[dict]) -> list[dict]:
return result
-@R.register("beam_auto_memory_step")
class BeamAutoMemoryStep(AutoMemoryStep):
"""AutoMemoryStep variant that interpolates timestamps for BEAM sessions.
diff --git a/reme/steps/benchmark/beam/auto_memory.yaml b/plugins/beam/src/reme_beam/auto_memory.yaml
similarity index 100%
rename from reme/steps/benchmark/beam/auto_memory.yaml
rename to plugins/beam/src/reme_beam/auto_memory.yaml
diff --git a/plugins/beam/src/reme_beam/plugin.yaml b/plugins/beam/src/reme_beam/plugin.yaml
new file mode 100644
index 00000000..389653fc
--- /dev/null
+++ b/plugins/beam/src/reme_beam/plugin.yaml
@@ -0,0 +1,120 @@
+backends:
+ beam_auto_memory_step: reme_beam.auto_memory:BeamAutoMemoryStep
+ beam_agentic_answer_step: reme_beam.agentic_answer:BeamAgenticAnswerStep
+ beam_rubric_judge_step: judge_beam.llm_judge:BeamRubricJudgeStep
+ beam_search_v2_step: reme_beam.search_v2:SearchV2Step
+
+application_defaults:
+ jobs:
+ search:
+ backend: base
+ description: "Hybrid workspace search (vector + BM25, RRF-fused) with deduplication."
+ parameters:
+ type: object
+ properties:
+ query:
+ type: string
+ description: "search query"
+ start_date:
+ type: string
+ description: "optional inclusive start date filter (YYYY-MM-DD); results earlier than this date are excluded"
+ end_date:
+ type: string
+ description: "optional inclusive end date filter (YYYY-MM-DD); results later than this date are excluded"
+ required:
+ - query
+ steps:
+ - backend: beam_search_v2_step
+ vector_weight: 0.7
+ candidate_multiplier: 5.0
+ expand_links: false
+ max_links_per_direction: 10
+
+ agentic_answer:
+ backend: base
+ description: "BEAM agentic answer job (ReAct agent with search tool)"
+ watch_dirs: []
+ watch_suffixes: []
+ parameters:
+ type: object
+ properties:
+ query:
+ type: string
+ description: "The query to ask"
+ query_time:
+ type: string
+ description: "ISO timestamp representing the query time"
+ default: ""
+ required:
+ - query
+ steps:
+ - backend: beam_agentic_answer_step
+ agent_wrapper: bench
+
+ answer_judge:
+ backend: base
+ description: "BEAM rubric-based LLM-as-Judge: evaluate response against rubric criteria"
+ watch_dirs: []
+ watch_suffixes: []
+ parameters:
+ type: object
+ properties:
+ llm_response:
+ type: string
+ description: "The model's response to evaluate"
+ rubric:
+ type: array
+ description: "List of rubric criteria to check"
+ items:
+ type: string
+ probing_question:
+ type: string
+ description: "The original probing question"
+ default: ""
+ question_type:
+ type: string
+ description: "BEAM question type (e.g. event_ordering)"
+ default: ""
+ required:
+ - llm_response
+ - rubric
+ steps:
+ - backend: beam_rubric_judge_step
+ agent_wrapper: judge
+
+ auto_memory:
+ backend: base
+ description: "Auto-memory: record conversation facts into a daily note"
+ parameters:
+ type: object
+ properties:
+ messages:
+ type: array
+ description: "messages"
+ items:
+ type: object
+ session_id:
+ type: string
+ description: "source conversation session identifier"
+ default: ""
+ memory_hint:
+ type: string
+ description: "optional hint"
+ date:
+ type: string
+ description: "YYYY-MM-DD daily note date; empty = infer from message timestamps or today"
+ default: ""
+ required:
+ - messages
+ steps:
+ - backend: beam_auto_memory_step
+ # Long sessions are split into turn-aligned segments and fed to the
+ # agent incrementally; <= 0 disables splitting.
+ max_segment_words: 10000
+
+ components:
+ as_llm:
+ default:
+ max_retries: 5
+ judge:
+ retry_delay: 5.0
diff --git a/reme/steps/index/search_v2.py b/plugins/beam/src/reme_beam/search_v2.py
similarity index 96%
rename from reme/steps/index/search_v2.py
rename to plugins/beam/src/reme_beam/search_v2.py
index 558eefc4..77c02027 100644
--- a/reme/steps/index/search_v2.py
+++ b/plugins/beam/src/reme_beam/search_v2.py
@@ -12,14 +12,18 @@ import datetime
import os
from typing import Final
-from ._dedup import _ToolContextDedupMixin
-from ._source_format import ALL_RETURNED_MESSAGE, NO_RESULTS_MESSAGE, is_session_path, join_chunk_entries
-from ._source_format import merge_session_chunk_intervals, render_chunk_entries
-from ..base_step import BaseStep
-from ..file_io import extract_daily_date
-from ...components import R
-from ...schema import FileChunk
-from ...utils import expand_links
+from reme.schema import FileChunk
+from reme.steps.base_step import BaseStep
+from reme.steps.file_io import extract_daily_date
+from reme.steps.index._dedup import _ToolContextDedupMixin
+from reme.steps.index._source_format import (
+ ALL_RETURNED_MESSAGE,
+ NO_RESULTS_MESSAGE,
+ is_session_path,
+ join_chunk_entries,
+)
+from reme.steps.index._source_format import merge_session_chunk_intervals, render_chunk_entries
+from reme.utils import expand_links
_RRF_K: Final = 60
_MAX_CANDIDATES: Final = 200
@@ -37,7 +41,6 @@ def _default_limit() -> int:
return _DEFAULT_LIMIT
-@R.register("search_v2_step")
class SearchV2Step(_ToolContextDedupMixin, BaseStep):
"""Hybrid search: run vector + keyword in parallel, fuse via RRF, filter, truncate."""
diff --git a/plugins/lme/LICENSE b/plugins/lme/LICENSE
new file mode 100644
index 00000000..65c2c5cf
--- /dev/null
+++ b/plugins/lme/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2025 Alibaba Group
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/plugins/lme/README.md b/plugins/lme/README.md
new file mode 100644
index 00000000..284ac7a4
--- /dev/null
+++ b/plugins/lme/README.md
@@ -0,0 +1,38 @@
+# LongMemEval plugin
+
+[中文说明](./README_ZH.md)
+
+This plugin owns the LongMemEval memory, agentic-answer and judge Steps, their prompts,
+and their Job defaults in `plugin.yaml`. ReMe's built-in `benchmark.yaml` owns the
+shared evaluation Jobs and components. Dataset handling, the runner and results remain
+in [`benchmark/longmemeval`](../../benchmark/longmemeval/README.md).
+
+From the repository root, install ReMe and this plugin in editable mode before running the benchmark:
+
+```bash
+python -m pip install -e ".[as]"
+reme plugins install ./plugins/lme --editable
+reme plugins validate lme
+python benchmark/longmemeval/run.py
+```
+
+Editable installation registers the `lme` entry point while keeping source changes immediately
+visible. The runner selects the built-in `benchmark` preset and explicitly enables `lme` for
+each Application. Installing the plugin makes it discoverable but does not enable it globally.
+
+`plugin.yaml` registers backends and contributes the plugin-owned `auto_memory`,
+`agentic_answer` and `answer_judge` Job defaults. Start the installed plugin with
+`reme start config=benchmark plugins='["lme"]'`. The shared preset does not inherit
+`default`: only declared Jobs run, indexing is manual, and neither scheduled dream
+nor the optional `auto_dream` Job is enabled.
+The existing `auto_memory`, `agentic_answer`, `answer_judge`, `bench` and `judge`
+names and model environment variables are unchanged. Explicit application/CLI overrides
+still take precedence. Installing this plugin does not start an evaluation.
+
+The shared answer base class lives in `reme.steps.benchmark.base_agentic_answer`.
+The old core-owned `reme.steps.benchmark.lme` Python import path is removed.
+Custom Python callers should import memory, search and answer Steps from `reme_lme`, and the
+judge Step from `judge_lme`. After uninstalling,
+Applications and CLI services must omit the plugin until it is installed again.
+Uninstallation never removes datasets, workspaces or results.
+Restart an existing service after changing plugins.
diff --git a/plugins/lme/README_ZH.md b/plugins/lme/README_ZH.md
new file mode 100644
index 00000000..176b2e80
--- /dev/null
+++ b/plugins/lme/README_ZH.md
@@ -0,0 +1,32 @@
+# LongMemEval 插件
+
+[English](./README.md)
+
+插件包含 LongMemEval 的记忆、回答、评分 Step、提示词,以及 `plugin.yaml` 中对应的 Job 默认配置。
+ReMe 内置的 `benchmark.yaml` 负责公共评测 Job 和 Component;数据集处理、runner 和结果仍留在
+[`benchmark/longmemeval`](../../benchmark/longmemeval/README_ZH.md)。
+
+在仓库根目录以 editable 模式安装 ReMe 和本插件,再运行评测:
+
+```bash
+python -m pip install -e ".[as]"
+reme plugins install ./plugins/lme --editable
+reme plugins validate lme
+python benchmark/longmemeval/run.py
+```
+
+editable 安装会注册 `lme` entry point,并让源码修改立即生效。runner 选择内置 `benchmark`
+配置,并为每个 Application 显式启用 `lme`。安装只让插件可被发现,不会在所有应用中全局启用。
+
+`plugin.yaml` 注册 backend,并通过 `application_defaults` 提供插件拥有的 `auto_memory`、
+`agentic_answer` 和 `answer_judge` Job。安装后使用
+`reme start config=benchmark plugins='["lme"]'`。公共评测配置不继承 `default`,只运行声明的 Job:
+索引手动更新,dream 定时任务和可选的 `auto_dream`
+均保持关闭。原有 `auto_memory`、`agentic_answer`、`answer_judge`、`bench`、`judge` 名称及模型环境变量
+保持不变,显式应用参数和 CLI 覆盖仍优先。安装或启用插件不会自动开始评测。
+
+共享回答基类位于 `reme.steps.benchmark.base_agentic_answer`。
+原 `reme.steps.benchmark.lme` Python 导入路径已移除。自定义 Python 调用应从 `reme_lme`
+导入记忆、搜索和回答 Step,并从 `judge_lme` 导入评判 Step。
+卸载插件后,Application 和 CLI 服务必须移除插件选择,直到再次安装。
+卸载不会删除数据集、工作区或结果。修改插件后需重启已有服务。
diff --git a/plugins/lme/pyproject.toml b/plugins/lme/pyproject.toml
new file mode 100644
index 00000000..fa91dd74
--- /dev/null
+++ b/plugins/lme/pyproject.toml
@@ -0,0 +1,26 @@
+[project]
+name = "reme-lme"
+version = "0.1.0"
+description = "LongMemEval benchmark plugin for ReMe."
+readme = "README.md"
+license = "Apache-2.0"
+license-files = ["LICENSE"]
+requires-python = ">=3.11"
+dependencies = [
+ "reme-ai[as]>=0.4.1.11",
+]
+
+[project.entry-points."reme.plugins"]
+lme = "reme_lme"
+
+[tool.setuptools.packages.find]
+where = ["src"]
+include = ["reme_lme*", "judge_lme*"]
+
+[tool.setuptools.package-data]
+reme_lme = ["plugin.yaml", "*.yaml"]
+judge_lme = ["*.yaml"]
+
+[build-system]
+requires = ["setuptools>=77", "wheel"]
+build-backend = "setuptools.build_meta"
diff --git a/plugins/lme/src/judge_lme/__init__.py b/plugins/lme/src/judge_lme/__init__.py
new file mode 100644
index 00000000..127e5621
--- /dev/null
+++ b/plugins/lme/src/judge_lme/__init__.py
@@ -0,0 +1,5 @@
+"""LongMemEval benchmark judge backend."""
+
+from .llm_judge import LmeAnswerJudgeStep
+
+__all__ = ["LmeAnswerJudgeStep"]
diff --git a/reme/steps/benchmark/lme/llm_judge.py b/plugins/lme/src/judge_lme/llm_judge.py
similarity index 96%
rename from reme/steps/benchmark/lme/llm_judge.py
rename to plugins/lme/src/judge_lme/llm_judge.py
index 9c13c5a5..10dbb06e 100644
--- a/reme/steps/benchmark/lme/llm_judge.py
+++ b/plugins/lme/src/judge_lme/llm_judge.py
@@ -2,11 +2,9 @@
import re
-from ...base_step import BaseStep
-from ....components import R
+from reme.steps.base_step import BaseStep
-@R.register("lme_answer_judge_step")
class LmeAnswerJudgeStep(BaseStep):
"""Evaluate whether an agent answer is correct against a golden answer."""
diff --git a/reme/steps/benchmark/lme/llm_judge.yaml b/plugins/lme/src/judge_lme/llm_judge.yaml
similarity index 100%
rename from reme/steps/benchmark/lme/llm_judge.yaml
rename to plugins/lme/src/judge_lme/llm_judge.yaml
diff --git a/reme/steps/benchmark/lme/__init__.py b/plugins/lme/src/reme_lme/__init__.py
similarity index 54%
rename from reme/steps/benchmark/lme/__init__.py
rename to plugins/lme/src/reme_lme/__init__.py
index 29005cc7..f1197900 100644
--- a/reme/steps/benchmark/lme/__init__.py
+++ b/plugins/lme/src/reme_lme/__init__.py
@@ -1,11 +1,11 @@
-"""LongMemEval benchmark steps."""
+"""LongMemEval benchmark backends and application configuration for ReMe."""
from .agentic_answer import LmeAgenticAnswerStep
-from .llm_judge import LmeAnswerJudgeStep
from .auto_memory import LmeAutoMemoryStep
+from .search_v2 import SearchV2Step
__all__ = [
"LmeAgenticAnswerStep",
- "LmeAnswerJudgeStep",
"LmeAutoMemoryStep",
+ "SearchV2Step",
]
diff --git a/reme/steps/benchmark/lme/agentic_answer.py b/plugins/lme/src/reme_lme/agentic_answer.py
similarity index 76%
rename from reme/steps/benchmark/lme/agentic_answer.py
rename to plugins/lme/src/reme_lme/agentic_answer.py
index 70f8926f..e9017e5c 100644
--- a/reme/steps/benchmark/lme/agentic_answer.py
+++ b/plugins/lme/src/reme_lme/agentic_answer.py
@@ -1,10 +1,8 @@
"""LongMemEval agentic answer step – ReAct agent that answers questions using the search tool."""
-from ....components import R
-from ..base import BaseAgenticAnswerStep
+from reme.steps.benchmark import BaseAgenticAnswerStep
-@R.register("lme_agentic_answer_step")
class LmeAgenticAnswerStep(BaseAgenticAnswerStep):
"""Answer a LongMemEval query via ReAct agent with access to the search tool.
@@ -12,7 +10,7 @@ class LmeAgenticAnswerStep(BaseAgenticAnswerStep):
``search`` job tool to retrieve relevant memory chunks before generating
a final answer.
- Session-transcript compression in ``search_v2_step`` is controlled by the
+ Session-transcript compression in the plugin's search Step is controlled by the
``compress_session`` flag in the runtime context (set by the benchmark
runner from ``evaluation.compress_session``); it is off by default.
"""
diff --git a/reme/steps/benchmark/lme/agentic_answer.yaml b/plugins/lme/src/reme_lme/agentic_answer.yaml
similarity index 100%
rename from reme/steps/benchmark/lme/agentic_answer.yaml
rename to plugins/lme/src/reme_lme/agentic_answer.yaml
diff --git a/reme/steps/benchmark/lme/auto_memory.py b/plugins/lme/src/reme_lme/auto_memory.py
similarity index 97%
rename from reme/steps/benchmark/lme/auto_memory.py
rename to plugins/lme/src/reme_lme/auto_memory.py
index bf3a678b..40c5b11c 100644
--- a/reme/steps/benchmark/lme/auto_memory.py
+++ b/plugins/lme/src/reme_lme/auto_memory.py
@@ -4,8 +4,7 @@ from datetime import datetime, timedelta
from agentscope.message import Msg
-from ...evolve.auto_memory import AutoMemoryStep, _normalize_msg_timestamp
-from ....components import R
+from reme.steps.evolve.auto_memory import AutoMemoryStep, _normalize_msg_timestamp
def _parse_iso_seconds(value: str) -> datetime | None:
@@ -115,7 +114,6 @@ def _interpolate_timestamps(items: list[dict]) -> list[dict]:
return result
-@R.register("lme_auto_memory_step")
class LmeAutoMemoryStep(AutoMemoryStep):
"""AutoMemoryStep variant that interpolates timestamps for LongMemEval sessions.
diff --git a/reme/steps/benchmark/lme/auto_memory.yaml b/plugins/lme/src/reme_lme/auto_memory.yaml
similarity index 100%
rename from reme/steps/benchmark/lme/auto_memory.yaml
rename to plugins/lme/src/reme_lme/auto_memory.yaml
diff --git a/plugins/lme/src/reme_lme/plugin.yaml b/plugins/lme/src/reme_lme/plugin.yaml
new file mode 100644
index 00000000..e8f9513c
--- /dev/null
+++ b/plugins/lme/src/reme_lme/plugin.yaml
@@ -0,0 +1,113 @@
+backends:
+ lme_auto_memory_step: reme_lme.auto_memory:LmeAutoMemoryStep
+ lme_agentic_answer_step: reme_lme.agentic_answer:LmeAgenticAnswerStep
+ lme_answer_judge_step: judge_lme.llm_judge:LmeAnswerJudgeStep
+ lme_search_v2_step: reme_lme.search_v2:SearchV2Step
+
+application_defaults:
+ jobs:
+ search:
+ backend: base
+ description: "Hybrid workspace search (vector + BM25, RRF-fused) with deduplication."
+ parameters:
+ type: object
+ properties:
+ query:
+ type: string
+ description: "search query"
+ start_date:
+ type: string
+ description: "optional inclusive start date filter (YYYY-MM-DD); results earlier than this date are excluded"
+ end_date:
+ type: string
+ description: "optional inclusive end date filter (YYYY-MM-DD); results later than this date are excluded"
+ required:
+ - query
+ steps:
+ - backend: lme_search_v2_step
+ vector_weight: 0.7
+ candidate_multiplier: 5.0
+ expand_links: false
+ max_links_per_direction: 10
+
+ agentic_answer:
+ backend: base
+ description: "LongMemEval agentic answer job (ReAct agent with search tool)"
+ watch_dirs: []
+ watch_suffixes: []
+ parameters:
+ type: object
+ properties:
+ query:
+ type: string
+ description: "The query to ask"
+ query_time:
+ type: string
+ description: "ISO timestamp representing the query time"
+ default: ""
+ required:
+ - query
+ steps:
+ - backend: lme_agentic_answer_step
+ agent_wrapper: bench
+
+ answer_judge:
+ backend: base
+ description: "LLM-as-Judge: evaluate agent answer against golden answer"
+ watch_dirs: []
+ watch_suffixes: []
+ parameters:
+ type: object
+ properties:
+ query:
+ type: string
+ description: "The question being asked"
+ agent_answer:
+ type: string
+ description: "The model's answer to evaluate"
+ golden_answer:
+ type: string
+ description: "The correct/golden answer"
+ question_type:
+ type: string
+ description: "The question type for prompt selection"
+ default: ""
+ required:
+ - query
+ - agent_answer
+ - golden_answer
+ steps:
+ - backend: lme_answer_judge_step
+ agent_wrapper: judge
+
+ auto_memory:
+ backend: base
+ description: "Auto-memory: record conversation facts into a daily note"
+ parameters:
+ type: object
+ properties:
+ messages:
+ type: array
+ description: "messages"
+ items:
+ type: object
+ session_id:
+ type: string
+ description: "source conversation session identifier"
+ default: ""
+ memory_hint:
+ type: string
+ description: "optional hint"
+ date:
+ type: string
+ description: "YYYY-MM-DD daily note date; empty = infer from message timestamps or today"
+ default: ""
+ required:
+ - messages
+ steps:
+ - backend: lme_auto_memory_step
+
+ components:
+ as_llm:
+ default:
+ max_retries: 3
diff --git a/plugins/lme/src/reme_lme/search_v2.py b/plugins/lme/src/reme_lme/search_v2.py
new file mode 100644
index 00000000..77c02027
--- /dev/null
+++ b/plugins/lme/src/reme_lme/search_v2.py
@@ -0,0 +1,333 @@
+"""Hybrid search (v2) over file_store using RRF fusion of vector + keyword results.
+
+This is the local fork of the upstream search step. It uses
+:class:`_ToolContextDedupMixin` for subset-aware interval-merging dedup and
+:func:`render_chunk_entries` / :func:`join_chunk_entries` for session-aware
+chunk formatting with
+:data:`ALL_RETURNED_MESSAGE` / :data:`NO_RESULTS_MESSAGE` notices.
+"""
+
+import asyncio
+import datetime
+import os
+from typing import Final
+
+from reme.schema import FileChunk
+from reme.steps.base_step import BaseStep
+from reme.steps.file_io import extract_daily_date
+from reme.steps.index._dedup import _ToolContextDedupMixin
+from reme.steps.index._source_format import (
+ ALL_RETURNED_MESSAGE,
+ NO_RESULTS_MESSAGE,
+ is_session_path,
+ join_chunk_entries,
+)
+from reme.steps.index._source_format import merge_session_chunk_intervals, render_chunk_entries
+from reme.utils import expand_links
+
+_RRF_K: Final = 60
+_MAX_CANDIDATES: Final = 200
+_DEFAULT_LIMIT_ENV: Final = "REME_SEARCH_LIMIT"
+_DEFAULT_LIMIT: Final = 5
+
+
+def _default_limit() -> int:
+ value = os.getenv(_DEFAULT_LIMIT_ENV)
+ if value is None:
+ return _DEFAULT_LIMIT
+ try:
+ return int(value)
+ except ValueError:
+ return _DEFAULT_LIMIT
+
+
+class SearchV2Step(_ToolContextDedupMixin, BaseStep):
+ """Hybrid search: run vector + keyword in parallel, fuse via RRF, filter, truncate."""
+
+ def __init__(
+ self,
+ *args,
+ seen_ttl_hours: float = 24,
+ **kwargs,
+ ):
+ super().__init__(*args, **kwargs)
+ self.seen_ttl_hours = seen_ttl_hours
+
+ @staticmethod
+ def _rrf_merge(
+ vector: list[FileChunk],
+ keyword: list[FileChunk],
+ vector_weight: float,
+ ) -> list[FileChunk]:
+ """Fuse two ranked lists with Reciprocal Rank Fusion, keyed by chunk.id."""
+ text_weight = 1.0 - vector_weight
+ merged: dict[str, FileChunk] = {}
+
+ for rank, chunk in enumerate(vector, start=1):
+ contrib = vector_weight / (_RRF_K + rank)
+ c = chunk.model_copy(deep=False)
+ c.scores = {**chunk.scores, "vector": chunk.scores.get("vector", chunk.score), "score": contrib}
+ merged[c.id] = c
+
+ for rank, chunk in enumerate(keyword, start=1):
+ contrib = text_weight / (_RRF_K + rank)
+ existing = merged.get(chunk.id)
+ if existing is not None:
+ existing.scores = {
+ **existing.scores,
+ "keyword": chunk.scores.get("keyword", chunk.score),
+ "score": existing.scores["score"] + contrib,
+ }
+ else:
+ c = chunk.model_copy(deep=False)
+ c.scores = {**chunk.scores, "keyword": chunk.scores.get("keyword", chunk.score), "score": contrib}
+ merged[c.id] = c
+
+ results = list(merged.values())
+ results.sort(key=lambda r: r.score, reverse=True)
+ return results
+
+ @staticmethod
+ def _format_scores(scores: dict[str, float], hybrid: bool) -> str:
+ """Format scores for the answer line: always show fused; show per-branch when hybrid."""
+ parts = [f"score={scores.get('score', 0.0):.4f}"]
+ if hybrid:
+ for k in ("vector", "keyword"):
+ v = scores.get(k)
+ parts.append(f"{k}={v:.4f}" if v is not None else f"{k}=-")
+ return " ".join(parts)
+
+ async def execute(self):
+ assert self.context is not None
+ query: str = (self.context.get("query", "") or "").strip()
+ limit: int = int(self.context.get("limit") or _default_limit())
+ min_score: float = float(self.context.get("min_score") or 0.0)
+ # vector_weight: prefer agent-supplied context value; fallback to YAML kwargs / default 0.7.
+ # Convertible numeric inputs are clipped to [0.0, 1.0]; non-numeric inputs are silently ignored.
+ raw_vw = self.context.get("vector_weight")
+ vector_weight: float | None = None
+ if raw_vw is not None:
+ try:
+ vector_weight = float(raw_vw)
+ except (TypeError, ValueError):
+ self.logger.warning(
+ f"[{self.name}] non-numeric vector_weight={raw_vw!r}; ignoring and using default 0.7",
+ )
+ vector_weight = None
+ if vector_weight is None:
+ vector_weight = float(self.kwargs.get("vector_weight", 0.7))
+ vector_weight = max(0.0, min(1.0, vector_weight))
+ candidate_multiplier: float = float(self.kwargs.get("candidate_multiplier", 5.0))
+ expand_links_enabled: bool = bool(self.kwargs.get("expand_links", True))
+ max_links_per_direction: int = int(self.kwargs.get("max_links_per_direction", 10))
+ tool_context_id: str = (self.context.get("tool_context_id", "") or "").strip()
+ # Injected value takes precedence over YAML kwargs; check existence
+ # (not truthiness) so an explicit False can disable a YAML-true flag.
+ _strict_date_filter = self.context.get("strict_date_filter")
+ if _strict_date_filter is None:
+ _strict_date_filter = self.kwargs.get("strict_date_filter", False)
+ strict_date_filter: bool = bool(_strict_date_filter)
+
+ if not query:
+ self.context.response.success = False
+ self.context.response.answer = "Error: query cannot be empty"
+ return self.context.response
+ assert limit > 0, f"limit must be positive, got {limit}"
+
+ candidates = min(_MAX_CANDIDATES, max(1, int(limit * candidate_multiplier)))
+ search_filter: dict = dict(self.context.get("search_filter", {}) or {})
+
+ # Promote top-level date parameters into search_filter for file_store.
+ for date_key in ("start_date", "end_date"):
+ value = self.context.get(date_key)
+ if value and date_key not in search_filter:
+ search_filter[date_key] = value
+
+ # Validate and normalize date filters before they reach file_store.
+ # _matches_search_filter does lexicographic string comparison against
+ # path_date (always a canonical YYYY-MM-DD), so raw caller values like
+ # "2026-2-28" or "abc" would produce silently wrong results.
+ for date_key in ("start_date", "end_date"):
+ raw = search_filter.get(date_key)
+ if raw is None:
+ continue
+ normalized = extract_daily_date(raw)
+ if normalized is None:
+ # Fallback: accept non-zero-padded dates like "2024-1-5".
+ try:
+ normalized = (
+ datetime.datetime.strptime(
+ str(raw).strip(),
+ "%Y-%m-%d",
+ )
+ .date()
+ .isoformat()
+ )
+ except ValueError:
+ self.logger.warning(
+ f"Ignoring invalid {date_key}={raw!r}; " f"expected a valid YYYY-MM-DD date.",
+ )
+ del search_filter[date_key]
+ continue
+ search_filter[date_key] = normalized
+
+ if strict_date_filter:
+ search_filter["strict_date_filter"] = True
+
+ vector_results, keyword_results = await asyncio.gather(
+ self.file_store.vector_search(query, candidates, search_filter),
+ self.file_store.keyword_search(query, candidates, search_filter),
+ )
+
+ self.logger.info(
+ f"[{self.name}] query={query!r} candidates={candidates} "
+ f"vector_hits={len(vector_results)} keyword_hits={len(keyword_results)}",
+ )
+
+ hybrid = bool(vector_results) and bool(keyword_results)
+ if not vector_results and not keyword_results:
+ fused: list[FileChunk] = []
+ elif not keyword_results:
+ fused = vector_results
+ elif not vector_results:
+ fused = keyword_results
+ else:
+ fused = self._rrf_merge(vector_results, keyword_results, vector_weight)
+
+ if min_score > 0.0:
+ fused = [c for c in fused if c.score >= min_score]
+
+ pre_dedup_count = 0
+ dedup: dict | None = None
+ if tool_context_id:
+ pre_dedup_count = len(fused)
+ fused, dedup = self._dedupe_tool_context(
+ fused,
+ tool_context_id,
+ limit,
+ clock=self.kwargs.get("clock"),
+ ttl_override=self.kwargs.get("tool_context_chunk_ttl_seconds"),
+ )
+ else:
+ fused = fused[:limit]
+
+ unique_paths = list(dict.fromkeys(c.path for c in fused))
+ link_expansion: dict[str, dict] = (
+ await expand_links(self.file_store, unique_paths, max_links_per_direction) if expand_links_enabled else {}
+ )
+
+ session_dir = self.config_value("session_dir")
+ entries = render_chunk_entries(
+ merge_session_chunk_intervals(fused, session_dir),
+ session_dir,
+ score_fn=lambda c: self._format_scores(c.scores, hybrid),
+ link_expansion=link_expansion,
+ )
+ if self._session_compress_enabled():
+ await self._compress_session_entries(entries, query, session_dir)
+ self.context.response.answer = join_chunk_entries(entries)
+ if not fused:
+ self.context.response.answer = ALL_RETURNED_MESSAGE if pre_dedup_count > 0 else NO_RESULTS_MESSAGE
+ self.context.response.metadata["results"] = [
+ c.model_dump(exclude_none=True, exclude={"embedding"}) for c in fused
+ ]
+ self.context.response.metadata["link_expansion"] = link_expansion
+ self.context.response.metadata["counts"] = {
+ "vector": len(vector_results),
+ "keyword": len(keyword_results),
+ "returned": len(fused),
+ "hybrid": hybrid,
+ }
+ if dedup is not None:
+ self.context.response.metadata["dedup"] = dedup
+ return self.context.response
+
+ def _session_compress_enabled(self) -> bool:
+ """True when the injected ``_search._compress.session`` flag is truthy."""
+ assert self.context is not None
+ search_cfg: dict = self.context.get("_search") or {}
+ value = (search_cfg.get("_compress") or {}).get("session")
+ return value is True or str(value).strip().lower() == "true"
+
+ async def _compress_session_entries(self, entries: list[dict[str, str]], query: str, session_dir: str) -> None:
+ """Compress session-transcript entry bodies in place via the ``compressor`` job.
+
+ Only entries whose ``path`` points at a raw session transcript are
+ compressed; other entries and all non-``body`` fields stay untouched.
+ When ``_search.type`` is ``query-independent`` the compressor runs
+ without queries (generic compression); otherwise (``query-aware``,
+ the default) it receives the injected ``_search.queries`` plus the
+ current search query.
+
+ The compressor receives the already-rendered body (one message per
+ line) stripped. Its output is adopted whenever the compressor
+ succeeded and the result is not longer than the input; adopted bodies
+ get a leading ``compressed session chunk:`` marker so downstream
+ consumers can tell them from verbatim transcripts.
+
+ Degrades gracefully when the ``compressor`` job is missing from the
+ active config: the whole method becomes a no-op and a warning is
+ logged, so search behaves as if compression were disabled. This
+ avoids a hard ``Job compressor not found`` failure when a benchmark
+ config forgets to define the compressor job/component.
+
+ Per-entry exceptions raised by the compressor job (e.g. a temporary
+ LLM outage) are caught inside ``compress`` so they never propagate
+ through ``asyncio.gather``: the failing entry keeps its original body
+ while the remaining entries are still compressed, preserving already
+ retrieved search results.
+ """
+ assert self.context is not None
+ # Guard: when the compressor job is missing from the active config
+ # (e.g. a benchmark config that forgot to define it), degrade
+ # gracefully to the no-compression behavior instead of raising
+ # "Job compressor not found" from run_job below.
+ # Skipped when there is no app_context (e.g. unit tests that mock
+ # run_job directly), so the mock can still drive compression.
+ if self.app_context is not None and self.get_job("compressor") is None:
+ self.logger.warning(
+ f"[{self.name}] compressor job not found in config; "
+ "skipping session chunk compression (degrading to uncompressed behavior)",
+ )
+ return
+ search_cfg: dict = self.context.get("_search") or {}
+ query_type = str(search_cfg.get("type") or "query-aware").strip().lower()
+ if query_type == "query-independent":
+ queries: list[str] = []
+ else:
+ queries = [str(q).strip() for q in (search_cfg.get("queries") or []) if str(q).strip()]
+ if query and query not in queries:
+ queries.append(query)
+
+ async def compress(entry: dict[str, str]) -> None:
+ path = entry.get("path", "")
+ body = (entry.get("body", "") or "").strip()
+ if not body:
+ return
+ try:
+ response = await self.run_job("compressor", text=body, queries=queries)
+ except Exception as exc: # pylint: disable=broad-except
+ self.logger.warning(
+ f"[{self.name}] session body compression raised path={path!r} " f"error={exc!r}; keeping original",
+ )
+ return
+ compressed = str(response.answer or "").strip()
+ if not response.success or not compressed:
+ self.logger.warning(
+ f"[{self.name}] session body compression failed path={path!r} "
+ f"success={response.success} answer={compressed[:100]!r}; keeping original",
+ )
+ return
+ if len(compressed) > len(body):
+ self.logger.info(
+ f"[{self.name}] compressed body longer than original "
+ f"({len(compressed)} > {len(body)}) path={path!r}; keeping original",
+ )
+ return
+ entry["body"] = f"compressed session chunk:\n{compressed}"
+
+ targets = [e for e in entries if is_session_path(e.get("path", ""), session_dir)]
+ if not targets:
+ return
+ self.logger.info(f"[{self.name}] compressing {len(targets)} session entries with {len(queries)} queries")
+ await asyncio.gather(*(compress(entry) for entry in targets))
diff --git a/reme/config/beam.yaml b/reme/config/beam.yaml
deleted file mode 100644
index c3d6bda1..00000000
--- a/reme/config/beam.yaml
+++ /dev/null
@@ -1,635 +0,0 @@
-# BEAM benchmark config — based on longmemeval.yaml (split)
-# All background/cron jobs are converted to base (manually callable).
-# Use with: resolve_app_config(config="beam.yaml", ...)
-
-service:
- backend: http
-
-jobs:
- # ── BEAM agentic answer (ReAct agent + search tool) ──
- agentic_answer:
- backend: base
- description: "BEAM agentic answer job (ReAct agent with search tool)"
- watch_dirs: []
- watch_suffixes: []
- parameters:
- type: object
- properties:
- query:
- type: string
- description: "The query to ask"
- query_time:
- type: string
- description: "ISO timestamp representing the query time"
- default: ""
- required:
- - query
- steps:
- - backend: beam_agentic_answer_step
- agent_wrapper: bench
-
- # ── BEAM rubric-based LLM-as-Judge ──
- answer_judge:
- backend: base
- description: "BEAM rubric-based LLM-as-Judge: evaluate response against rubric criteria"
- watch_dirs: []
- watch_suffixes: []
- parameters:
- type: object
- properties:
- llm_response:
- type: string
- description: "The model's response to evaluate"
- rubric:
- type: array
- description: "List of rubric criteria to check"
- items:
- type: string
- probing_question:
- type: string
- description: "The original probing question"
- default: ""
- question_type:
- type: string
- description: "BEAM question type (e.g. event_ordering)"
- default: ""
- required:
- - llm_response
- - rubric
- steps:
- - backend: beam_rubric_judge_step
- agent_wrapper: judge
-
- # ── Manual index update (replaces index_update_loop background) ──
- index_update:
- backend: base
- description: "Manually trigger incremental index update for watched dirs."
- watch_dirs: [daily_dir, digest_dir, session_dir/dialog]
- watch_suffixes: [md, jsonl]
- parameters:
- type: object
- properties: {}
- steps:
- - backend: init_changes_step
- monitor_type: file_store
- monitor_name: default
- dispatch_steps: [update_index_step]
-
- # ── Manual digest catalog update (replaces digest_watch_loop background) ──
- digest_update:
- backend: base
- description: "Manually trigger digest catalog update."
- watch_dirs: [daily_dir, digest_dir]
- watch_suffixes: [md]
- parameters:
- type: object
- properties: {}
- steps:
- - backend: init_changes_step
- monitor_type: file_catalog
- monitor_name: digest
- dispatch_steps:
- - backend: update_catalog_step
- file_catalog: digest
- - backend: log_changes_step
-
- # ── Auto dream (same as default.yaml auto_dream, base mode) ──
- # auto_dream:
- # backend: base
- # description: "Auto-dream: scan today's day-index and daily notes, globally extract merged units/topics, integrate digest units, write interests.yaml, and persist the dream catalog."
- # parameters:
- # type: object
- # properties:
- # date:
- # type: string
- # description: "YYYY-MM-DD to scan; defaults to today in the dreamer's timezone"
- # default: ""
- # hint:
- # type: string
- # description: "caller guidance passed through to dream extract/integrate"
- # default: ""
- # scan_days:
- # type: integer
- # description: "number of recent daily directories to scan, ending at date"
- # default: 2
- # max_units:
- # type: integer
- # description: "maximum number of extracted memory units"
- # default: 5
- # topic_count:
- # type: integer
- # description: "maximum number of final daily interest topics"
- # default: 3
- # topic_diversity_days:
- # type: integer
- # description: "number of previous interests.yaml days to avoid repeating"
- # default: 7
- # steps:
- # - backend: dream_extract_step
- # file_catalog: dream
- # topic_session_id: interests
- # scan_days: 2
- # max_units: 5
- # - backend: dream_integrate_step
- # - backend: dream_topics_step
- # topic_count: 3
- # topic_diversity_days: 7
- # - backend: dream_finish_step
- # file_catalog: dream
-
- # ── Auto memory (same as default.yaml) ──
- auto_memory:
- backend: base
- description: "Auto-memory: record conversation facts into a daily note"
- parameters:
- type: object
- properties:
- messages:
- type: array
- description: "messages"
- items:
- type: object
- session_id:
- type: string
- description: "source conversation session identifier"
- default: ""
- memory_hint:
- type: string
- description: "optional hint"
- date:
- type: string
- description: "YYYY-MM-DD daily note date; empty = infer from message timestamps or today"
- default: ""
- required:
- - messages
- steps:
- - backend: beam_auto_memory_step
- # Long sessions are split into turn-aligned segments and fed to the
- # agent incrementally; each segment holds at most this many words.
- # ("segment" here is a slice of the message list, unrelated to file
- # chunking in the index.) <= 0 disables splitting.
- max_segment_words: 10000
-
- # ── Text compression (direct LLM call, no agent) ──
- compressor:
- backend: base
- description: "Compress text via a direct LLM call, optionally guided by queries as relevance filter"
- parameters:
- type: object
- properties:
- text:
- type: string
- description: "the text to compress"
- queries:
- type: array
- description: "optional list of queries; content potentially relevant to any query is kept, content certainly irrelevant to all queries may be dropped"
- items:
- type: string
- default: []
- required:
- - text
- steps:
- - backend: compressor_step
- as_llm: compressor
-
- # ── Reindex (derived search indexes only) ──
- reindex:
- backend: base
- description: "rebuild BM25 and/or embedding indexes from current file_chunks"
- parameters:
- type: object
- properties:
- scope:
- type: string
- enum: [all, bm25, embedding]
- default: all
- steps:
- - backend: reindex_step
-
- # ── Search ──
- # start_date:
- # type: string
- # description: "optional inclusive start date filter (YYYY-MM-DD); results earlier than this date are excluded"
- # end_date:
- # type: string
- # description: "optional inclusive end date filter (YYYY-MM-DD); results later than this date are excluded"
-
- search:
- backend: base
- description: "Hybrid workspace search (vector + BM25, RRF-fused) with deduplication."
- parameters:
- type: object
- properties:
- query:
- type: string
- description: "search query"
- start_date:
- type: string
- description: "optional inclusive start date filter (YYYY-MM-DD); results earlier than this date are excluded"
- end_date:
- type: string
- description: "optional inclusive end date filter (YYYY-MM-DD); results later than this date are excluded"
- # vector_weight:
- # type: number
- # description: >-
- # Optional weight balancing vector similarity vs BM25 keyword matching in the
- # RRF fusion. Recommended value is 0.7, which provides a good balance between
- # semantic (vector) similarity and lexical (BM25) matching. Values close to 0
- # emphasize BM25 keyword matching, values close to 1 emphasize vector semantic
- # similarity.
- required:
- - query
- steps:
- - backend: search_v2_step
- vector_weight: 0.7
- candidate_multiplier: 5.0
- expand_links: false
- max_links_per_direction: 10
-
- add_draft:
- backend: base
- description: "Append text to the current draft list."
- parameters:
- type: object
- properties:
- text:
- type: string
- description: "draft text to append"
- required:
- - text
- steps:
- - backend: add_draft_step
-
- read_all_draft:
- backend: base
- description: "Read all draft text previously appended in the current tool context."
- parameters:
- type: object
- properties: { }
- steps:
- - backend: read_all_draft_step
-
- python_execute:
- backend: base
- description: "Execute Python code and return printed stdout."
- parameters:
- type: object
- properties:
- code:
- type: string
- description: "Python code to execute. Print the final result to stdout."
- timeout:
- type: number
- description: "Execution timeout in seconds; defaults to 60."
- required:
- - code
- steps:
- - backend: python_execute_step
-
- # ── File I/O jobs (needed by auto_memory agent tools) ──
- daily_list:
- backend: base
- description: "List notes under a single day."
- parameters:
- type: object
- properties:
- date:
- type: string
- description: "YYYY-MM-DD; empty = today"
- default: ""
- steps:
- - backend: daily_list_step
-
- daily_reindex:
- backend: base
- description: "Rebuild the day-index page daily/.md."
- parameters:
- type: object
- properties:
- date:
- type: string
- description: "YYYY-MM-DD; empty = today"
- default: ""
- steps:
- - backend: daily_reindex_step
-
- frontmatter_update:
- backend: base
- description: "Merge key-values into a file's frontmatter."
- parameters:
- type: object
- properties:
- path:
- type: string
- description: "workspace-relative path"
- metadata:
- type: object
- description: "key-values to merge"
- required:
- - path
- - metadata
- steps:
- - backend: frontmatter_update_step
-
- move:
- backend: base
- description: "Move / rename a workspace file."
- parameters:
- type: object
- properties:
- src_path:
- type: string
- description: "workspace-relative source"
- dst_path:
- type: string
- description: "workspace-relative destination"
- overwrite:
- type: boolean
- default: false
- retarget:
- type: boolean
- default: true
- required:
- - src_path
- - dst_path
- steps:
- - backend: move_step
-
- read:
- backend: base
- description: "Read a markdown file under the workspace."
- parameters:
- type: object
- properties:
- path:
- type: string
- description: "workspace-relative path; markdown only"
- start_line:
- type: integer
- end_line:
- type: integer
- required:
- - path
- steps:
- - backend: read_step
- with_neighbors: false
- max_neighbors_per_direction: 10
-
- write:
- backend: base
- description: "Write a markdown file."
- parameters:
- type: object
- properties:
- path:
- type: string
- name:
- type: string
- description:
- type: string
- content:
- type: string
- metadata:
- type: object
- required:
- - path
- - name
- - description
- - content
- steps:
- - backend: write_step
-
- daily_write:
- backend: base
- description: "Write a daily markdown note."
- parameters:
- type: object
- properties:
- name:
- type: string
- description:
- type: string
- session_id:
- type: string
- content:
- type: string
- date:
- type: string
- default: ""
- metadata:
- type: object
- required:
- - name
- - description
- - session_id
- - content
- steps:
- - backend: daily_write_step
-
- edit:
- backend: base
- description: "Find-and-replace in a markdown file."
- parameters:
- type: object
- properties:
- path:
- type: string
- old:
- type: string
- new:
- type: string
- default: ""
- required:
- - path
- - old
- - new
- steps:
- - backend: edit_step
-
- frontmatter_read:
- backend: base
- description: "Read a file's frontmatter as a dict."
- parameters:
- type: object
- properties:
- path:
- type: string
- required:
- - path
- steps:
- - backend: frontmatter_read_step
-
- node_search:
- backend: base
- description: "Digest node recall."
- parameters:
- type: object
- properties:
- query:
- type: string
- limit:
- type: integer
- default: 20
- required:
- - query
- steps:
- - backend: node_search_step
- vector_weight: 0.7
- candidate_multiplier: 5.0
-
-components:
- tokenizer:
- default:
- backend: regex
-
- as_embedding:
- default:
- backend: ${EMBEDDING_BACKEND:-openai}
- model: ${EMBEDDING_MODEL_NAME:-text-embedding-v4}
- credential:
- api_key: ${EMBEDDING_API_KEY:-}
- base_url: ${EMBEDDING_BASE_URL:-https://dashscope.aliyuncs.com/compatible-mode/v1}
- dimensions: 1024
-
- embedding_store:
- default:
- backend: local
- as_embedding: default
-
- as_llm:
- default:
- backend: ${LLM_BACKEND:-openai}
- model: ${LLM_MODEL_NAME:-qwen3.6-flash}
- stream: true
- context_size: 200000
- max_retries: 5
- retry_delay: 5.0
- credential:
- api_key: ${LLM_API_KEY:-}
- base_url: ${LLM_BASE_URL:-}
- parameters:
- max_tokens: 65536
- thinking_enable: false
- judge:
- backend: ${LLM_BACKEND:-openai}
- model: ${JUDGE_MODEL_NAME:-qwen3.7-max}
- stream: false
- context_size: 200000
- max_retries: 5
- retry_delay: 5.0
- credential:
- api_key: ${LLM_API_KEY:-}
- base_url: ${LLM_BASE_URL:-}
- parameters:
- max_tokens: 65536
- thinking_enable: false
- bench:
- backend: ${LLM_BACKEND:-openai}
- model: ${BENCH_MODEL_NAME:-qwen3.7-max}
- stream: true
- context_size: 400000
- max_retries: 5
- retry_delay: 5.0
- credential:
- api_key: ${LLM_API_KEY:-}
- base_url: ${LLM_BASE_URL:-}
- parameters:
- max_tokens: 65536
- thinking_enable: true
- compressor:
- backend: ${LLM_BACKEND:-openai}
- model: ${LLM_MODEL_NAME:-qwen3.6-flash}
- stream: false
- context_size: 200000
- max_retries: 5
- retry_delay: 5.0
- credential:
- api_key: ${LLM_API_KEY:-}
- base_url: ${LLM_BASE_URL:-}
- parameters:
- max_tokens: 65536
- thinking_enable: false
-
- agent_wrapper:
- default:
- backend: agentscope
- as_llm: default
- permission_mode: bypass
- react_config:
- max_iters: 30
- context_config:
- trigger_ratio: 0.8
- reserve_ratio: 0.1
- tool_result_limit: 50000
- model_config:
- max_retries: 1
- judge:
- backend: agentscope
- as_llm: judge
- permission_mode: bypass
- react_config:
- max_iters: 1
- context_config:
- trigger_ratio: 0.8
- reserve_ratio: 0.1
- tool_result_limit: 50000
- model_config:
- max_retries: 1
- bench:
- backend: agentscope
- as_llm: bench
- permission_mode: bypass
- react_config:
- max_iters: 30
- context_config:
- trigger_ratio: 0.8
- reserve_ratio: 0.1
- tool_result_limit: 50000
- model_config:
- max_retries: 1
-
- file_graph:
- default:
- backend: local
-
- file_catalog:
- default:
- backend: local
- resource:
- backend: local
- digest:
- backend: local
- dream:
- backend: local
-
- file_chunker:
- markdown:
- backend: markdown
- supported_extensions: [ "md" ]
- embed_toc: true
- max_ast_sections: 100
- include_frontmatter_in_metadata: false
- include_frontmatter_keys_in_metadata: [] # empty = all non-empty frontmatter keys
- json:
- backend: json
- supported_extensions: [ "json" ]
- jsonl:
- backend: jsonl
- supported_extensions: [ "jsonl" ] # noqa: keep #314 chunker scope intact after #325
- max_chars: 4000
- default:
- backend: default
- supported_extensions: ["txt","log"]
-
- keyword_index:
- default:
- backend: bm25
- tokenizer: default
-
- file_store:
- default:
- backend: local
- store_name: local
- embedding_store: default
- keyword_index: default
- file_graph: default
diff --git a/reme/config/lme.yaml b/reme/config/benchmark.yaml
similarity index 74%
rename from reme/config/lme.yaml
rename to reme/config/benchmark.yaml
index 24ec4e40..643c3a62 100644
--- a/reme/config/lme.yaml
+++ b/reme/config/benchmark.yaml
@@ -1,63 +1,11 @@
-# LongMemEval benchmark config — based on longmemeval.yaml (split)
-# All background/cron jobs are converted to base (manually callable).
-# Use with: resolve_app_config(config="lme.yaml", ...)
+# Shared benchmark application preset; independent of the default service config.
+# Background and cron jobs are omitted in favor of manually callable base jobs.
+# Benchmark plugins contribute their own jobs through plugin.yaml.
service:
backend: http
jobs:
- # ── LongMemEval agentic answer (ReAct agent + search tool) ──
- agentic_answer:
- backend: base
- description: "LongMemEval agentic answer job (ReAct agent with search tool)"
- watch_dirs: []
- watch_suffixes: []
- parameters:
- type: object
- properties:
- query:
- type: string
- description: "The query to ask"
- query_time:
- type: string
- description: "ISO timestamp representing the query time"
- default: ""
- required:
- - query
- steps:
- - backend: lme_agentic_answer_step
- agent_wrapper: bench
-
- # ── LLM-as-Judge for evaluating answers ──
- answer_judge:
- backend: base
- description: "LLM-as-Judge: evaluate agent answer against golden answer"
- watch_dirs: []
- watch_suffixes: []
- parameters:
- type: object
- properties:
- query:
- type: string
- description: "The question being asked"
- agent_answer:
- type: string
- description: "The model's answer to evaluate"
- golden_answer:
- type: string
- description: "The correct/golden answer"
- question_type:
- type: string
- description: "The question type for prompt selection"
- default: ""
- required:
- - query
- - agent_answer
- - golden_answer
- steps:
- - backend: lme_answer_judge_step
- agent_wrapper: judge
-
# ── Manual index update (replaces index_update_loop background) ──
index_update:
backend: base
@@ -135,34 +83,6 @@ jobs:
# - backend: dream_finish_step
# file_catalog: dream
- # ── Auto memory (same as default.yaml) ──
- auto_memory:
- backend: base
- description: "Auto-memory: record conversation facts into a daily note"
- parameters:
- type: object
- properties:
- messages:
- type: array
- description: "messages"
- items:
- type: object
- session_id:
- type: string
- description: "source conversation session identifier"
- default: ""
- memory_hint:
- type: string
- description: "optional hint"
- date:
- type: string
- description: "YYYY-MM-DD daily note date; empty = infer from message timestamps or today"
- default: ""
- required:
- - messages
- steps:
- - backend: lme_auto_memory_step
-
# ── Text compression (direct LLM call, no agent) ──
compressor:
backend: base
@@ -199,46 +119,6 @@ jobs:
steps:
- backend: reindex_step
- # ── Search ──
- # start_date:
- # type: string
- # description: "optional inclusive start date filter (YYYY-MM-DD); results earlier than this date are excluded"
- # end_date:
- # type: string
- # description: "optional inclusive end date filter (YYYY-MM-DD); results later than this date are excluded"
-
- search:
- backend: base
- description: "Hybrid workspace search (vector + BM25, RRF-fused) with deduplication."
- parameters:
- type: object
- properties:
- query:
- type: string
- description: "search query"
- start_date:
- type: string
- description: "optional inclusive start date filter (YYYY-MM-DD); results earlier than this date are excluded"
- end_date:
- type: string
- description: "optional inclusive end date filter (YYYY-MM-DD); results later than this date are excluded"
- # vector_weight:
- # type: number
- # description: >-
- # Optional weight balancing vector similarity vs BM25 keyword matching in the
- # RRF fusion. Recommended value is 0.7, which provides a good balance between
- # semantic (vector) similarity and lexical (BM25) matching. Values close to 0
- # emphasize BM25 keyword matching, values close to 1 emphasize vector semantic
- # similarity.
- required:
- - query
- steps:
- - backend: search_v2_step
- vector_weight: 0.7
- candidate_multiplier: 5.0
- expand_links: false
- max_links_per_direction: 10
-
add_draft:
backend: base
description: "Append text to the current draft list."
@@ -326,7 +206,7 @@ jobs:
move:
backend: base
- description: "Move / rename a workspace file."
+ description: "Move / rename a workspace file; rewrites inbound wikilinks by default."
parameters:
type: object
properties:
@@ -338,9 +218,11 @@ jobs:
description: "workspace-relative destination"
overwrite:
type: boolean
+ description: "overwrite if dst exists"
default: false
retarget:
type: boolean
+ description: "rewrite [[src]] → [[dst]] across the workspace"
default: true
required:
- src_path
@@ -359,8 +241,10 @@ jobs:
description: "workspace-relative path; markdown only"
start_line:
type: integer
+ description: "first line (1-based, inclusive)"
end_line:
type: integer
+ description: "last line (1-based, inclusive)"
required:
- path
steps:
@@ -370,20 +254,25 @@ jobs:
write:
backend: base
- description: "Write a markdown file."
+ description: "Write a markdown file (create or overwrite) with name/description frontmatter."
parameters:
type: object
properties:
path:
type: string
+ description: "workspace-relative path; markdown only"
name:
type: string
+ description: "frontmatter name"
description:
type: string
+ description: "frontmatter description"
content:
type: string
+ description: "body"
metadata:
type: object
+ description: "Optional extra frontmatter fields (md only)."
required:
- path
- name
@@ -394,23 +283,29 @@ jobs:
daily_write:
backend: base
- description: "Write a daily markdown note."
+ description: "Write a daily markdown note with conversation source frontmatter."
parameters:
type: object
properties:
name:
type: string
+ description: "daily note filename stem and frontmatter name"
description:
type: string
+ description: "frontmatter description"
session_id:
type: string
+ description: "source conversation session identifier"
content:
type: string
+ description: "body"
date:
type: string
+ description: "YYYY-MM-DD daily note date; empty = today"
default: ""
metadata:
type: object
+ description: "Optional extra frontmatter fields."
required:
- name
- description
@@ -421,16 +316,19 @@ jobs:
edit:
backend: base
- description: "Find-and-replace in a markdown file."
+ description: "Find-and-replace in a markdown file (all occurrences)."
parameters:
type: object
properties:
path:
type: string
+ description: "workspace-relative path"
old:
type: string
+ description: "text to find"
new:
type: string
+ description: "replacement"
default: ""
required:
- path
@@ -447,6 +345,7 @@ jobs:
properties:
path:
type: string
+ description: "workspace-relative path"
required:
- path
steps:
@@ -454,14 +353,16 @@ jobs:
node_search:
backend: base
- description: "Digest node recall."
+ description: "Digest node recall — given a candidate abstraction's name+description, surface existing digest nodes similar enough to either dedup against or link to as related."
parameters:
type: object
properties:
query:
type: string
+ description: "search query"
limit:
type: integer
+ description: "max digest nodes to return"
default: 20
required:
- query
@@ -495,7 +396,6 @@ components:
model: ${LLM_MODEL_NAME:-qwen3.6-flash}
stream: true
context_size: 200000
- max_retries: 3
retry_delay: 5.0
credential:
api_key: ${LLM_API_KEY:-}
diff --git a/reme/steps/benchmark/__init__.py b/reme/steps/benchmark/__init__.py
index a5fdc8aa..c0d50414 100644
--- a/reme/steps/benchmark/__init__.py
+++ b/reme/steps/benchmark/__init__.py
@@ -1,17 +1,5 @@
-"""Benchmark steps."""
+"""Shared benchmark steps; concrete implementations live in plugins."""
-from . import base, lme, beam
-from .base import BaseAgenticAnswerStep
-from .lme import LmeAgenticAnswerStep, LmeAnswerJudgeStep
-from .beam import BeamAgenticAnswerStep, BeamRubricJudgeStep
+from .base_agentic_answer import BaseAgenticAnswerStep
-__all__ = [
- "BaseAgenticAnswerStep",
- "LmeAgenticAnswerStep",
- "LmeAnswerJudgeStep",
- "BeamAgenticAnswerStep",
- "BeamRubricJudgeStep",
- "base",
- "lme",
- "beam",
-]
+__all__ = ["BaseAgenticAnswerStep"]
diff --git a/reme/steps/benchmark/base/__init__.py b/reme/steps/benchmark/base/__init__.py
deleted file mode 100644
index 2b31bb0d..00000000
--- a/reme/steps/benchmark/base/__init__.py
+++ /dev/null
@@ -1,7 +0,0 @@
-"""Shared base classes for benchmark steps."""
-
-from .agentic_answer import BaseAgenticAnswerStep
-
-__all__ = [
- "BaseAgenticAnswerStep",
-]
diff --git a/reme/steps/benchmark/base/agentic_answer.py b/reme/steps/benchmark/base_agentic_answer.py
similarity index 92%
rename from reme/steps/benchmark/base/agentic_answer.py
rename to reme/steps/benchmark/base_agentic_answer.py
index 53c854f4..bcd5c8f6 100644
--- a/reme/steps/benchmark/base/agentic_answer.py
+++ b/reme/steps/benchmark/base_agentic_answer.py
@@ -2,14 +2,14 @@
import os
-from ...base_step import BaseStep
-from ...index._dedup import _ToolContextDedupMixin
-from ....enumeration import ChunkEnum
-from ....utils.counter import global_counter_inc
+from ..base_step import BaseStep
+from ..index._dedup import _ToolContextDedupMixin
+from ...enumeration import ChunkEnum
+from ...utils.counter import global_counter_inc
class BaseAgenticAnswerStep(BaseStep):
- """Base ReAct-agent answer step shared by BEAM and LongMemEval benchmarks.
+ """ReAct-agent answer implementation shared by benchmark plugins.
Subclasses only need to set:
TOOL_CONTEXT_PREFIX (str): prefix used to build the unique tool_context_id.
@@ -18,7 +18,7 @@ class BaseAgenticAnswerStep(BaseStep):
tool call via ``injected_job_kwargs``; override the attribute or the
``_injected_job_kwargs`` hook to customize.
- And apply their own ``@R.register(...)`` decorator and docstring.
+ Concrete subclasses are registered by the plugin manifest.
Inputs (from RuntimeContext):
query (str, required): The question to answer.
@@ -43,7 +43,7 @@ class BaseAgenticAnswerStep(BaseStep):
``INJECTED_JOB_KWARGS`` with per-request values derived from ``query``.
When the runtime context carries a truthy ``compress_session`` flag,
- session-transcript compression is enabled in ``search_v2_step`` by
+ session-transcript compression is enabled in the benchmark plugin's search Step by
injecting a ``_search._compress.session`` marker plus the current
``query`` as the query-aware relevance filter. Default (falsy) leaves
session chunks uncompressed.
diff --git a/reme/steps/index/__init__.py b/reme/steps/index/__init__.py
index b46dc9c5..b5dbb59e 100644
--- a/reme/steps/index/__init__.py
+++ b/reme/steps/index/__init__.py
@@ -12,7 +12,6 @@ from .init_changes import InitChangesStep
from .optimize_index import OptimizeIndexStep
from .reindex import ReindexStep
from .search import SearchStep
-from .search_v2 import SearchV2Step
from .traverse import TraverseStep
from .update_changes import ChangeApplyStep, UpdateCatalogStep, UpdateIndexStep
from .vector_search import VectorSearchStep
@@ -42,7 +41,6 @@ __all__ = [
"ReindexStep",
"OptimizeIndexStep",
"SearchStep",
- "SearchV2Step",
"TraverseStep",
"UpdateCatalogStep",
"UpdateIndexStep",
diff --git a/tests/unit/test_beam_auto_memory.py b/tests/unit/test_beam_auto_memory.py
deleted file mode 100644
index bddb3e79..00000000
--- a/tests/unit/test_beam_auto_memory.py
+++ /dev/null
@@ -1,128 +0,0 @@
-"""Unit tests for BEAM auto-memory segmented ingestion and line numbering."""
-
-# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access
-
-from agentscope.message import Msg
-
-from reme.components.runtime_context import RuntimeContext
-from reme.steps.benchmark.beam.auto_memory import (
- BeamAutoMemoryStep,
- split_turn_segments,
-)
-
-
-def _msg(role: str, words: int, created_at: str = "2024-03-01T09:00:00") -> Msg:
- return Msg(
- name=role,
- role=role,
- content=[{"type": "text", "text": " ".join(["w"] * words)}],
- created_at=created_at,
- )
-
-
-def _dialog(n_turns: int, words_per_msg: int) -> list[Msg]:
- messages: list[Msg] = []
- for _ in range(n_turns):
- messages.append(_msg("user", words_per_msg))
- messages.append(_msg("assistant", words_per_msg))
- return messages
-
-
-class TestSplitTurnSegments:
- def test_empty(self):
- assert not split_turn_segments([], 100)
-
- def test_disabled_returns_single_segment(self):
- messages = _dialog(3, 10)
- segments = split_turn_segments(messages, 0)
- assert len(segments) == 1
- assert segments[0] == (0, messages)
-
- def test_under_limit_single_segment(self):
- messages = _dialog(3, 10) # 60 words total
- segments = split_turn_segments(messages, 100)
- assert len(segments) == 1
- assert segments[0][0] == 0
- assert segments[0][1] == messages
-
- def test_splits_at_turn_boundaries(self):
- # 4 turns x 20 words each; limit 40 -> 2 turns per segment
- messages = _dialog(4, 10)
- segments = split_turn_segments(messages, 40)
- assert len(segments) == 2
- offsets = [offset for offset, _ in segments]
- assert offsets == [0, 4]
- # Every segment starts with a user message and ends with an assistant
- for _, segment in segments:
- assert segment[0].role == "user"
- assert segment[-1].role == "assistant"
- # No message lost or duplicated, order preserved
- flattened = [m for _, segment in segments for m in segment]
- assert flattened == messages
-
- def test_never_splits_inside_a_turn(self):
- # One turn alone exceeds the limit -> becomes its own oversized segment
- messages = [
- _msg("user", 5),
- _msg("assistant", 5),
- _msg("user", 50),
- _msg("assistant", 50), # 100-word turn > limit 60
- _msg("user", 5),
- _msg("assistant", 5),
- ]
- segments = split_turn_segments(messages, 60)
- assert [offset for offset, _ in segments] == [0, 2, 4]
- assert [len(segment) for _, segment in segments] == [2, 2, 2]
-
- def test_offsets_are_original_indices(self):
- messages = _dialog(5, 30) # 60 words per turn
- segments = split_turn_segments(messages, 120)
- # 2 turns per segment -> offsets 0, 4, 8
- assert [offset for offset, _ in segments] == [0, 4, 8]
- for offset, segment in segments:
- for i, msg in enumerate(segment):
- assert msg is messages[offset + i]
-
- def test_multi_assistant_turn_stays_together(self):
- messages = [
- _msg("user", 10),
- _msg("assistant", 10),
- _msg("assistant", 10),
- _msg("user", 10),
- _msg("assistant", 10),
- ]
- segments = split_turn_segments(messages, 30)
- assert [offset for offset, _ in segments] == [0, 3]
- assert len(segments[0][1]) == 3
-
-
-class TestFormatHistoryLineNumbers:
- def _step(self, offset: int) -> BeamAutoMemoryStep:
- step = BeamAutoMemoryStep(name="beam_auto_memory_step", backend="beam_auto_memory_step")
- step.context = RuntimeContext(
- session_id="beam_1M_1_batch1",
- beam_line_offset=offset,
- )
- return step
-
- def test_numbers_start_at_one_without_offset(self):
- step = self._step(0)
- history = step._format_history(_dialog(2, 3))
- assert "[L1 | user @" in history
- assert "[L4 | assistant @" in history
- assert "lines 1-4" in history
- assert "session/dialog/beam_1M_1_batch1.jsonl" in history
-
- def test_numbers_use_original_file_offset(self):
- step = self._step(40)
- history = step._format_history(_dialog(2, 3))
- assert "[L41 | user @" in history
- assert "[L44 | assistant @" in history
- assert "lines 41-44" in history
- assert "[L1 " not in history
-
- def test_build_messages_passes_msg_objects_through(self):
- step = self._step(0)
- messages = _dialog(2, 3)
- rebuilt = step._build_messages(messages)
- assert [m.id for m in rebuilt] == [m.id for m in messages]
diff --git a/tests/unit/test_injected_job_kwargs.py b/tests/unit/test_injected_job_kwargs.py
index 847b2757..d92482c5 100644
--- a/tests/unit/test_injected_job_kwargs.py
+++ b/tests/unit/test_injected_job_kwargs.py
@@ -230,7 +230,7 @@ async def test_auto_memory_update_scopes_tools_to_exact_note_path(tmp_path, monk
def test_auto_memory_keeps_original_tool_names():
- """BEAM/LME configs define only the original jobs; no *_daily variants exist."""
+ """Core auto-memory uses the original file tool names."""
step = AutoMemoryStep(name="auto_memory")
assert step.create_tools == ["daily_write"]
assert step.update_tools == ["read", "edit", "frontmatter_update", "write"]
@@ -240,11 +240,7 @@ def test_auto_memory_create_prompts_match_upstream_date_arguments():
"""Auto-memory prompts keep the upstream model-supplied date argument."""
from pathlib import Path
- prompt_files = (
- Path("reme/steps/evolve/auto_memory.yaml"),
- Path("reme/steps/benchmark/beam/auto_memory.yaml"),
- Path("reme/steps/benchmark/lme/auto_memory.yaml"),
- )
+ prompt_files = (Path("reme/steps/evolve/auto_memory.yaml"),)
for prompt_file in prompt_files:
content = prompt_file.read_text(encoding="utf-8")
assert "date={today}" in content or "`date`: {today}" in content or "`date`:{today}" in content
@@ -253,7 +249,7 @@ def test_auto_memory_create_prompts_match_upstream_date_arguments():
def test_configs_define_original_jobs_without_daily_variants():
from reme.config import resolve_app_config
- for config_name in ("default", "lme", "beam"):
+ for config_name in ("default",):
config = resolve_app_config(config=config_name, log_config=False)
jobs = config["jobs"]
for name in ("read", "edit", "write", "frontmatter_update", "daily_write"):
diff --git a/tests/unit/test_plugin.py b/tests/unit/test_plugin.py
index a86aaa15..595b32e0 100644
--- a/tests/unit/test_plugin.py
+++ b/tests/unit/test_plugin.py
@@ -43,6 +43,22 @@ def _set_entry_points(monkeypatch, *entries):
monkeypatch.setattr("reme.entry_point.metadata.entry_points", lambda: _FakeEntryPoints(entries))
+@pytest.mark.parametrize("name", ["lme", "beam"])
+def test_shared_benchmark_preset_is_builtin_but_plugin_aliases_and_backends_are_not(monkeypatch, name):
+ _set_entry_points(monkeypatch)
+ benchmark = _load_config("benchmark")
+ assert {"index_update", "digest_update", "read", "write"} <= benchmark["jobs"].keys()
+ assert {"search", "auto_memory", "agentic_answer", "answer_judge"}.isdisjoint(benchmark["jobs"])
+ for alias in (name, f"{name}.yaml"):
+ with pytest.raises(FileNotFoundError, match="Config file not found"):
+ _load_config(alias)
+ assert R.get(ComponentEnum.STEP, f"{name}_auto_memory_step") is None
+ assert R.get(ComponentEnum.STEP, f"{name}_agentic_answer_step") is None
+ assert R.get(ComponentEnum.STEP, f"{name}_search_v2_step") is None
+ judge = "lme_answer_judge_step" if name == "lme" else "beam_rubric_judge_step"
+ assert R.get(ComponentEnum.STEP, judge) is None
+
+
def test_plugin_application_defaults_are_below_application_config():
manager = PluginManager(
[
diff --git a/tests/unit/test_search_step.py b/tests/unit/test_search_step.py
index 67040f8b..65824d55 100644
--- a/tests/unit/test_search_step.py
+++ b/tests/unit/test_search_step.py
@@ -1,6 +1,8 @@
-"""Unit tests for SearchV2Step without embedding or LLM dependencies."""
+"""Unit tests for workspace search Steps without embedding or LLM dependencies."""
import asyncio
+import importlib.util
+from pathlib import Path
from agentscope.message import Msg
@@ -14,11 +16,17 @@ from reme.steps.index import (
Bm25SearchStep,
ReadAllDraftStep,
SearchStep,
- SearchV2Step,
VectorSearchStep,
)
from reme.steps.index._source_format import ALL_RETURNED_MESSAGE, NO_RESULTS_MESSAGE
+_SEARCH_V2_PATH = Path(__file__).parents[2] / "plugins" / "beam" / "src" / "reme_beam" / "search_v2.py"
+_SEARCH_V2_SPEC = importlib.util.spec_from_file_location("reme_beam.search_v2", _SEARCH_V2_PATH)
+assert _SEARCH_V2_SPEC is not None and _SEARCH_V2_SPEC.loader is not None
+_SEARCH_V2_MODULE = importlib.util.module_from_spec(_SEARCH_V2_SPEC)
+_SEARCH_V2_SPEC.loader.exec_module(_SEARCH_V2_MODULE)
+SearchV2Step = _SEARCH_V2_MODULE.SearchV2Step
+
class FakeSearchStore(BaseFileStore):
"""Minimal file_store for SearchV2Step: static search results and empty graph links."""
diff --git a/typescript/README.md b/typescript/README.md
index 19ec084c..9357adf1 100644
--- a/typescript/README.md
+++ b/typescript/README.md
@@ -2,145 +2,51 @@
[中文说明](./README_ZH.md)
-`@agentscope-ai/reme` provides one shared ReMe HTTP client and host adapters for DeepSeek Harness and OpenClaw. Each
-adapter uses its host's native lifecycle and tool interfaces; importing the root package does not load either host.
+`@agentscope-ai/reme` connects DeepSeek Harness and OpenClaw to ReMe's local-first, file-native long-term memory. The package also exposes a host-independent ReMe HTTP client.
-The package expects a running ReMe HTTP service with the `search`, `auto_memory`, and `auto_dream` jobs required by the
-selected adapter:
+
+
+## Capabilities
+
+- Injects memory guidance and provides explicit `reme_search` lookup.
+- Captures completed conversations through background `auto_memory` batches.
+- Runs optional daily `auto_dream` consolidation in the workspace timezone.
+- Keeps durable memory in user-owned `daily` and `digest` Markdown files.
+- Uses each host's native lifecycle, tools, settings, and shutdown hooks.
+- Excludes plugin context and tool results from automatic memory capture.
+
+## Documentation
+
+| Host | English | 中文 |
+| ---------------- | --------------------------- | ------------------------------------ |
+| DeepSeek Harness | [Guide](./docs/dsh.md) | [使用指南](./docs/dsh.zh-CN.md) |
+| OpenClaw | [Guide](./docs/openclaw.md) | [使用指南](./docs/openclaw.zh-CN.md) |
+
+## Quick start
+
+Start ReMe:
```bash
+pip install "reme-ai[core]"
reme start workspace_dir=/absolute/path/to/workspace
```
-The default endpoint is `http://127.0.0.1:2333`. All entries support `REME_URL`, or `REME_HOST` plus `REME_PORT`.
-ReMe's HTTP service does not use API-key authentication.
-
-## Requirements
-
-- A running ReMe HTTP service with the jobs required by the selected adapter.
-- Node.js `22.22.3+`, `24.15.0+`, or `25.9.0+` on the corresponding supported major-version line.
-- DeepSeek Harness or OpenClaw only when using that host-specific entry; the root client has no host runtime dependency.
-
-## DeepSeek Harness
-
-Install the package as a DSH profile bundle:
+Install the adapter for your host:
```bash
+# DeepSeek Harness
dsh plugin --profile web add @agentscope-ai/reme
+
+# OpenClaw
+openclaw plugins install clawhub:@agentscope-ai/reme
```
-The bundle loads `@agentscope-ai/reme/dsh` in an isolated `remeMemory` realm. It injects durable memory guidance,
-registers `reme_search`, submits completed main-agent turns to `auto_memory`, and runs the optional daily `auto_dream`
-schedule. Recalled plugin context and tool results are excluded from automatic memory capture.
+The default endpoint is `http://127.0.0.1:2333`. ReMe HTTP does not use API-key authentication, so keep it on loopback or another trusted network unless it is protected by a proxy.
-Configure the bundle by replacing its row in the profile's `cordis.patch.yml`:
-
-```yaml
-- id: reme-memory
- config:
- - id: reme-memory-runtime
- name: "@agentscope-ai/reme/dsh"
- config:
- endpoint: http://127.0.0.1:2333
- language: zh
- timezone: Asia/Shanghai
- autoMemoryInterval: 5
- autoDreamEnabled: true
- dreamCron: "0 23 * * *"
-```
-
-On the DSH Web profile, the same fields are available under **Settings → Plugins → Plugin configuration → ReMe
-Memory**. Changes are stored in DSH's user settings document and apply to subsequent requests and captures. Changing
-the daily dream controls reschedules the next run; changing the guidance language affects newly started sessions.
-The test-only `dreamIntervalMs` value remains outside the user-settings section.
-
-| Option | Default | Meaning |
-| --------------------- | ----------------------- | --------------------------------------------- |
-| `endpoint` | `http://127.0.0.1:2333` | ReMe HTTP service URL |
-| `language` | `en` | Memory guidance language: `en` or `zh` |
-| `autoMemoryEnabled` | `true` | Capture completed main-agent turns |
-| `autoMemoryInterval` | `5` | Submit after this many completed turns |
-| `autoDreamEnabled` | `true` | Enable daily dream maintenance |
-| `dreamCron` | `0 23 * * *` | Daily schedule in the workspace timezone |
-| `dreamHint` | empty | Optional guidance sent to `auto_dream` |
-| `rootAgentsOnly` | `true` | Exclude subagents from guidance and capture |
-| `searchLimit` | `5` | Maximum results returned by `reme_search` |
-| `requestTimeoutMs` | `10000` | Search request timeout |
-| `backgroundTimeoutMs` | `3600000` | Automatic-memory and dream timeout |
-| `shutdownTimeoutMs` | `5000` | Best-effort shutdown drain budget |
-| `timezone` | `Asia/Shanghai` | IANA timezone used for batches and scheduling |
-
-The ReMe card reads the service's `health_check` and `status` jobs on demand. It shows the ReMe version, component
-health, chunk/index counts, process RSS, and estimated component memory; it can also display the redacted `app_config`
-response and trigger one `auto_dream` run. Diagnostics are refreshed when the card first opens or when the user asks,
-not polled continuously. The page calls the configured ReMe HTTP endpoint from the local browser, so that service must
-remain browser-reachable and allow the DSH origin.
-
-## OpenClaw
-
-OpenClaw `2026.7.1` or later can install the same package. The current SDK and
-OpenClaw Gateway require Node.js `22.22.3+`, `24.15.0+`, or `25.9.0+` on their
-respective major-version lines:
-
-```bash
-openclaw plugins install @agentscope-ai/reme
-```
-
-Select `reme` for `plugins.slots.memory` when another memory plugin is active. The adapter uses OpenClaw's current
-`before_prompt_build` hook, registers the `reme_search` action, injects durable memory guidance, and recalls relevant
-memory before conversational root-agent runs. Completed user/assistant pairs are grouped into per-session,
-date-consistent batches for `auto_memory`; failed batches are retained for retry and pending work is flushed within a
-bounded Gateway shutdown budget. One plugin-owned daily schedule runs `auto_dream`. Recall is wrapped in
-`` and marked as untrusted historical data. Cron, heartbeat, memory, overflow, and subagent runs do not
-recall or capture conversational memory by default.
-
-OpenClaw plugin configuration accepts:
-
-| Option | Default | Meaning |
-| --------------------- | ----------------------- | --------------------------------------------- |
-| `endpoint` | `http://127.0.0.1:2333` | ReMe HTTP service URL |
-| `language` | `en` | Memory guidance language: `en` or `zh` |
-| `autoRecall` | `true` | Recall before conversational root-agent runs |
-| `searchLimit` | `5` | Maximum search results |
-| `recallMinScore` | `0` | Minimum search score |
-| `autoMemoryEnabled` | `true` | Capture completed conversational turns |
-| `autoMemoryInterval` | `5` | Submit after this many completed turns |
-| `autoDreamEnabled` | `true` | Enable daily memory consolidation |
-| `dreamCron` | `0 23 * * *` | Daily schedule in the workspace timezone |
-| `dreamHint` | empty | Optional guidance sent to `auto_dream` |
-| `rootAgentsOnly` | `true` | Exclude subagents from guidance and capture |
-| `timezone` | `Asia/Shanghai` | IANA timezone used for batches and scheduling |
-| `requestTimeoutMs` | `10000` | Recall and explicit search timeout |
-| `backgroundTimeoutMs` | `3600000` | Automatic-memory and dream timeout |
-| `shutdownTimeoutMs` | `5000` | Best-effort shutdown drain budget |
-
-OpenClaw's conversation-access and prompt-injection permissions remain host settings; enable them for ReMe when your
-OpenClaw policy requires explicit grants. The adapter does not modify Gateway configuration.
-
-## Library entry
-
-Consumers that only need the transport can import the root package:
+## Client library
```ts
import { ReMeClient, formatReMeContext } from "@agentscope-ai/reme";
```
-Host code is available only through `@agentscope-ai/reme/dsh` and `@agentscope-ai/reme/openclaw`.
-
-## Development
-
-```bash
-cd typescript
-npm ci
-npm run format:check
-npm run lint
-npm run typecheck
-npm test
-npm run test:package
-```
-
-`npm pack` runs the TypeScript build and includes only `dist`, the DSH patch, the OpenClaw manifest, and the English and
-Chinese READMEs. `npm run pack:clawhub -- ` creates the ClawHub artifact with the same runtime files
-and the OpenClaw-only `README_OPENCLAW.md` and `README_OPENCLAW_ZH.md` content at the package README paths.
-
-Stable releases use npm's `latest` distribution tag; prereleases use `next`.
+Host adapters are exported from `@agentscope-ai/reme/dsh` and `@agentscope-ai/reme/openclaw`. See the host guides for requirements, configuration, screenshots, troubleshooting, and release behavior.
diff --git a/typescript/README_ZH.md b/typescript/README_ZH.md
index 3e81cf29..5b108fac 100644
--- a/typescript/README_ZH.md
+++ b/typescript/README_ZH.md
@@ -1,136 +1,52 @@
# 面向 TypeScript Agent 的 ReMe
-`@agentscope-ai/reme` 提供统一的 ReMe HTTP 客户端,以及 DeepSeek Harness(DSH)和 OpenClaw 适配器。每个适配器都使用宿主原生的生命周期与工具接口;导入包根入口不会加载任何宿主适配器。
+[English](./README.md)
-使用前需要启动 ReMe HTTP 服务,并确保所选适配器需要的 `search`、`auto_memory` 和 `auto_dream` 任务可用:
+`@agentscope-ai/reme` 将 DeepSeek Harness 和 OpenClaw 连接到 ReMe 本地优先、文件原生的长期记忆,并提供不依赖宿主的 ReMe HTTP 客户端。
+
+
+
+## 核心能力
+
+- 注入长期记忆使用指引,并提供显式的 `reme_search` 检索。
+- 通过后台 `auto_memory` 批次沉淀已完成的对话。
+- 按 workspace 时区运行可选的每日 `auto_dream` 记忆整理。
+- 将持久记忆保存在用户拥有的 `daily` 和 `digest` Markdown 文件中。
+- 使用各宿主原生的生命周期、工具、设置和关停 Hook。
+- 自动记忆会排除插件上下文和工具结果,避免循环写回。
+
+## 使用文档
+
+| 宿主 | English | 中文 |
+| ---------------- | --------------------------- | ------------------------------------ |
+| DeepSeek Harness | [Guide](./docs/dsh.md) | [使用指南](./docs/dsh.zh-CN.md) |
+| OpenClaw | [Guide](./docs/openclaw.md) | [使用指南](./docs/openclaw.zh-CN.md) |
+
+## 快速开始
+
+启动 ReMe:
```bash
+pip install "reme-ai[core]"
reme start workspace_dir=/absolute/path/to/workspace
```
-默认服务地址为 `http://127.0.0.1:2333`。所有入口均支持 `REME_URL`,也支持组合使用 `REME_HOST` 和 `REME_PORT`。ReMe HTTP 服务不使用 API Key 认证。
-
-## 环境要求
-
-- 已启动 ReMe HTTP 服务,并提供所选适配器需要的 Job。
-- 对应受支持主版本线上的 Node.js `22.22.3+`、`24.15.0+` 或 `25.9.0+`。
-- 只有使用宿主专用入口时才需要 DeepSeek Harness 或 OpenClaw;包根 client 不依赖宿主 runtime。
-
-## DeepSeek Harness
-
-将本包安装为 DSH profile bundle:
+安装对应宿主的适配器:
```bash
+# DeepSeek Harness
dsh plugin --profile web add @agentscope-ai/reme
+
+# OpenClaw
+openclaw plugins install clawhub:@agentscope-ai/reme
```
-安装后可在 **设置 → 插件 → 插件配置 → ReMe Memory** 中配置服务地址、记忆指引语言、自动记忆、每日记忆整理和超时时间,并查看服务健康状态。每日记忆整理和每日批次统一使用配置的 workspace 时区。
-
-Bundle 会在隔离的 `remeMemory` realm 中加载 `@agentscope-ai/reme/dsh`,注入长期记忆指引,注册
-`reme_search`,将主 Agent 已完成的对话提交给 `auto_memory`,并按可选的每日计划运行 `auto_dream`。召回的
-plugin context 和工具结果不会再次进入自动记忆。
-
-如需直接修改 profile,可以替换 `cordis.patch.yml` 中对应条目:
-
-```yaml
-- id: reme-memory
- config:
- - id: reme-memory-runtime
- name: "@agentscope-ai/reme/dsh"
- config:
- endpoint: http://127.0.0.1:2333
- language: zh
- timezone: Asia/Shanghai
- autoMemoryInterval: 5
- autoDreamEnabled: true
- dreamCron: "0 23 * * *"
-```
-
-DSH Web profile 也可以在 **设置 → 插件 → 插件配置 → ReMe Memory** 中编辑同一组配置。设置会保存到 DSH
-用户设置文档,并从后续请求和捕获开始生效;修改每日计划会重新调度下一次任务,修改语言只影响新会话。
-仅供测试的 `dreamIntervalMs` 不属于用户设置。
-
-| 配置项 | 默认值 | 作用 |
-| --------------------- | ----------------------- | ------------------------------- |
-| `endpoint` | `http://127.0.0.1:2333` | ReMe HTTP 服务地址 |
-| `language` | `en` | 记忆指引语言:`en` 或 `zh` |
-| `autoMemoryEnabled` | `true` | 捕获主 Agent 已完成的对话 |
-| `autoMemoryInterval` | `5` | 每完成多少轮提交一次 |
-| `autoDreamEnabled` | `true` | 启用每日记忆整理 |
-| `dreamCron` | `0 23 * * *` | workspace 时区下的每日计划 |
-| `dreamHint` | 空字符串 | 传给 `auto_dream` 的可选指引 |
-| `rootAgentsOnly` | `true` | 不为子 Agent 注入指引或捕获对话 |
-| `searchLimit` | `5` | `reme_search` 返回结果上限 |
-| `requestTimeoutMs` | `10000` | 搜索请求超时 |
-| `backgroundTimeoutMs` | `3600000` | 自动记忆和 Auto Dream 超时 |
-| `shutdownTimeoutMs` | `5000` | 退出时尽力排空任务的时间预算 |
-| `timezone` | `Asia/Shanghai` | 每日批次和计划使用的 IANA 时区 |
-
-ReMe 配置卡会按需调用 `health_check` 和 `status`,显示 ReMe 版本、组件健康状态、chunk/index 数量、进程 RSS
-与组件内存估算;它也可以显示脱敏后的 `app_config`,并手动触发一次 `auto_dream`。页面首次打开或用户主动刷新时才会
-更新诊断,不会持续轮询。浏览器必须能够访问所配置的 ReMe HTTP 地址,且 ReMe 服务需要允许 DSH 页面所在的 origin。
-
-## OpenClaw
-
-OpenClaw `2026.7.1` 或更高版本可以直接安装本包。当前 SDK 和 Gateway 支持各主版本线上的
-Node.js `22.22.3+`、`24.15.0+` 或 `25.9.0+`:
-
-```bash
-openclaw plugins install @agentscope-ai/reme
-```
-
-当其他记忆插件已启用时,请将 `plugins.slots.memory` 设为 `reme`。适配器使用最新的
-`before_prompt_build` Hook,注册 `reme_search` Action,并为根 Agent 注入记忆使用指引和相关历史。
-已完成的用户/助手消息按会话、日期分批提交给 `auto_memory`;失败批次会保留重试,Gateway 退出时会在有限时间内刷新。
-插件还会按 workspace 时区运行一份每日 `auto_dream` 计划。默认不会处理子 Agent、Cron、Heartbeat、Memory 或 Overflow 触发的运行。
-
-召回内容包裹在 `` 中并标记为不可信历史数据。OpenClaw 插件配置如下:
-
-| 配置项 | 默认值 | 作用 |
-| --------------------- | ----------------------- | ------------------------------------ |
-| `endpoint` | `http://127.0.0.1:2333` | ReMe HTTP 服务地址 |
-| `language` | `en` | 记忆指引语言:`en` 或 `zh` |
-| `autoRecall` | `true` | 在根 Agent 对话运行前自动召回 |
-| `searchLimit` | `5` | 搜索结果上限 |
-| `recallMinScore` | `0` | 自动召回的最低搜索分数 |
-| `autoMemoryEnabled` | `true` | 捕获已完成的对话 |
-| `autoMemoryInterval` | `5` | 每完成多少轮提交一次 |
-| `autoDreamEnabled` | `true` | 启用每日记忆整理 |
-| `dreamCron` | `0 23 * * *` | workspace 时区下的每日计划 |
-| `dreamHint` | 空字符串 | 传给 `auto_dream` 的可选指引 |
-| `rootAgentsOnly` | `true` | 不为子 Agent 注入指引或捕获对话 |
-| `timezone` | `Asia/Shanghai` | 每日批次和计划使用的 IANA 时区 |
-| `requestTimeoutMs` | `10000` | 自动召回和显式搜索超时 |
-| `backgroundTimeoutMs` | `3600000` | 自动记忆和 Auto Dream 超时 |
-| `shutdownTimeoutMs` | `5000` | Gateway 退出时尽力排空任务的时间预算 |
-
-OpenClaw 的会话访问和 Prompt 注入权限仍由宿主侧配置;策略要求显式授权时,需要为 ReMe 开启相应权限。本适配器不会修改
-Gateway 配置。
+默认地址为 `http://127.0.0.1:2333`。ReMe HTTP 不使用 API Key 认证;除非前面有受保护的代理,否则应只监听 loopback 或其他可信网络。
## 客户端库
-仅需要 HTTP 客户端时,可以从包根入口导入:
-
```ts
import { ReMeClient, formatReMeContext } from "@agentscope-ai/reme";
```
-宿主适配器分别通过 `@agentscope-ai/reme/dsh` 和 `@agentscope-ai/reme/openclaw` 提供。
-
-## 开发与发布检查
-
-```bash
-cd typescript
-npm ci
-npm run format:check
-npm run lint
-npm run typecheck
-npm test
-npm run test:package
-```
-
-`npm pack` 会先构建 TypeScript,并且只打包 `dist`、DSH patch、OpenClaw manifest 和中英文 README。
-`npm run pack:clawhub -- <输出目录>` 会使用相同的运行时文件生成 ClawHub 产物,并将仅包含 OpenClaw
-说明的 `README_OPENCLAW.md` 和 `README_OPENCLAW_ZH.md` 放到包内 README 路径。
-
-稳定版本使用 npm 的 `latest` distribution tag 发布,预发布版本使用 `next`。
+宿主适配器分别从 `@agentscope-ai/reme/dsh` 和 `@agentscope-ai/reme/openclaw` 导出。环境要求、完整配置、截图、排障和发布行为请查看对应的宿主文档。
diff --git a/typescript/docs/dsh.md b/typescript/docs/dsh.md
new file mode 100644
index 00000000..cac2b204
--- /dev/null
+++ b/typescript/docs/dsh.md
@@ -0,0 +1,224 @@
+# ReMe plugin guide for DeepSeek Harness
+
+[中文说明](./dsh.zh-CN.md)
+
+This guide explains how to install, configure, and use `@agentscope-ai/reme` with DeepSeek Harness (DSH), including memory guidance injection, the `reme_search` tool, automatic memory, daily consolidation, and the ReMe Status page.
+
+The screenshots come from a real local integration test. DSH uses the `default` workspace, both the interface and ReMe guidance are set to English, and ReMe uses an isolated temporary workspace containing only the fictional Project Polaris test data. No `.env` values, API keys, or personal memories appear in the screenshots.
+
+## 1. How the plugin works
+
+When a DSH session starts, the plugin injects guidance that tells the root agent when and how to use long-term memory. It also registers the read-only `reme_search` tool. After a turn completes, the plugin can submit user and assistant messages to ReMe `auto_memory`; a daily schedule can run `auto_dream` to consolidate journal entries into durable personal knowledge.
+
+```text
+New session
+ └─ Inject long-term-memory guidance
+ └─ Agent decides whether the request depends on history
+ └─ reme_search → ReMe search → daily / digest files
+
+Completed conversation
+ └─ Automatic-memory batch → ReMe auto_memory → daily files
+ └─ Scheduled consolidation → ReMe auto_dream → digest files
+```
+
+The DSH adapter injects **usage guidance**, not every historical memory. Relevant memories enter the conversation only when the agent calls `reme_search`. This keeps unrelated history out of the prompt and helps prevent historical content from being treated as new instructions.
+
+## 2. Requirements
+
+- ReMe is installed and its configuration exposes the `search`, `auto_memory`, and `auto_dream` jobs.
+- DeepSeek Harness `0.1.2-rc.1` or later.
+- Node.js `22.22.3+`, `24.15.0+`, or `25.9.0+` on the corresponding supported major-version line.
+- The browser running DSH can reach the configured ReMe HTTP endpoint. Cross-machine deployments must also allow the DSH browser origin.
+
+The default ReMe endpoint is `http://127.0.0.1:2333`. ReMe HTTP does not use API-key authentication, so do not expose it directly to an untrusted network.
+
+## 3. Install and start
+
+### 3.1 Start ReMe
+
+```bash
+reme start workspace_dir=/absolute/path/to/your/reme-workspace
+```
+
+For development and screenshots, use an isolated directory outside the repository, such as `/tmp/reme-dsh-demo`. Do not write runtime memory into the repository's `.reme/` directory.
+
+### 3.2 Install the DSH bundle
+
+Install the published package:
+
+```bash
+dsh plugin --profile web add @agentscope-ai/reme
+```
+
+For local package development:
+
+```bash
+cd /path/to/deepseek-harness
+pnpm link /path/to/ReMe/typescript --workspace-root
+dsh plugin --profile web add @agentscope-ai/reme
+```
+
+The package declares `dsh/cordis.patch.yml` through `package.json#dsh.bundle.patch`. The patch loads `@agentscope-ai/reme/dsh` and the Web client in an isolated `remeMemory` realm, following the DSH `0.1.2-rc.1` plugin protocol.
+
+### 3.3 Start DSH Web
+
+```bash
+dsh web --no-open --port 3080
+```
+
+Open the local URL printed by DSH and select the `default` workspace. If DSH enables an access token, use the authenticated URL from its startup output and do not copy the token into documentation or screenshots.
+
+## 4. Configure ReMe Memory
+
+Open **Settings → Plugins → Plugin configuration → ReMe Memory**. Save changes before starting the next session. Settings are stored in DSH's user settings document and apply to subsequent requests and captures. A language change affects new sessions; a schedule change immediately reschedules the next consolidation.
+
+
+
+| UI meaning | Configuration key | Default | Description |
+| ---------------------- | --------------------- | ----------------------- | --------------------------------------------------------------- |
+| Service URL | `endpoint` | `http://127.0.0.1:2333` | Absolute ReMe HTTP URL using `http` or `https`. |
+| Guidance language | `language` | `en` | `en` or `zh`; controls guidance injected into new sessions. |
+| Default search results | `searchLimit` | `5` | Default `reme_search` result limit, from 1 to 50. |
+| Search timeout | `requestTimeoutMs` | `10000` | Search timeout in milliseconds, from 1,000 to 120,000. |
+| Automatic memory | `autoMemoryEnabled` | `true` | Capture completed user/assistant turns for `auto_memory`. |
+| Exclude subagents | `rootAgentsOnly` | `true` | Inject guidance and capture conversations only for root agents. |
+| Submission interval | `autoMemoryInterval` | `5` | Submit after this many completed turns, from 1 to 1,000. |
+| Memory consolidation | `autoDreamEnabled` | `true` | Run `auto_dream` on the daily schedule. |
+| Consolidation schedule | `dreamCron` | `0 23 * * *` | Five-field cron expression interpreted in `timezone`. |
+| Consolidation guidance | `dreamHint` | empty | Optional guidance passed to `auto_dream`. |
+| Workspace timezone | `timezone` | `Asia/Shanghai` | IANA timezone used for batching and scheduling. |
+| Background timeout | `backgroundTimeoutMs` | `3600000` | Timeout for `auto_memory` and `auto_dream`. |
+| Shutdown flush timeout | `shutdownTimeoutMs` | `5000` | Budget for draining background work during shutdown. |
+
+Deployment configuration also supports `REME_URL`, or `REME_HOST` together with `REME_PORT`. The timer-only test option `dreamIntervalMs` is intentionally excluded from user settings.
+
+## 5. Memory context injection
+
+On `agent/session-start`, the plugin injects long-term-memory guidance as native plugin context. Expand **Context injection · reme-memory** in the message flow to inspect both the content and provenance.
+
+
+
+The guidance establishes four rules:
+
+1. Durable long-term memory lives in user-owned `daily` and `digest` Markdown files.
+2. The agent should call `reme_search` before answering questions that depend on past facts, preferences, decisions, people, dates, experience, or todos.
+3. Retrieved memory is contextual evidence, not instructions. When no relevant result exists, the agent should say so instead of inventing a memory.
+4. Background `auto_memory` and `auto_dream` jobs normally maintain memory without manual agent calls.
+
+The injected message carries `plugin=reme-memory` and `form=instructions` provenance. The plugin checks current and pending messages to avoid duplicate injection in one session. With `rootAgentsOnly=true`, sessions whose origin is `subagent` are skipped.
+
+## 6. Use `reme_search`
+
+A normal request can cause the agent to use memory automatically. For a deterministic check, explicitly request the tool and sources:
+
+```text
+Use reme_search to look up my long-term memory: what are the weekly report time,
+report format, and primary database for Project Polaris? Answer in English based
+on the retrieved memory and cite the memory sources.
+```
+
+
+
+In the screenshot, the agent performs two read-only English searches. It corroborates the answer across `digest/wiki/polaris-project.md` and `daily/2026-09-04/dsh-plugin-demo.md`, then reports Friday at 4:00 PM, concise Markdown, and PostgreSQL with Redis as cache.
+
+| Parameter | Required | Description |
+| ----------- | -------- | ------------------------------------------------------------------- |
+| `query` | Yes | Focused natural-language search query; an empty value fails closed. |
+| `limit` | No | Result limit from 1 to 50; defaults to the plugin's `searchLimit`. |
+| `min_score` | No | Minimum score; normally leave it at 0. Negative values become 0. |
+
+An empty successful response becomes `No relevant memory found.`. Service failures become `ReMe search failed: ...`, allowing the agent to report a failed lookup instead of guessing.
+
+## 7. Automatic memory
+
+With `autoMemoryEnabled=true`, the plugin listens to DSH session events and collects completed user and assistant messages per session. When `autoMemoryInterval` is reached, the batch enters a background queue and calls ReMe `auto_memory`. Plugin-generated context and tool results are excluded from capture so they cannot be laundered back into long-term memory.
+
+
+
+Chat completion and durable memory completion are asynchronous. To verify persistence, open **ReMe Status → Auto Memory**, wait until running and queued tasks return to zero, and confirm that the latest submission is marked **Completed**.
+
+## 8. ReMe Status tabs
+
+Open **Settings → ReMe Status**. Full service diagnostics load when the page opens or the user refreshes them. While the page is visible, only the DSH plugin runtime counters refresh every 5 seconds.
+
+### 8.1 Overview
+
+
+
+Overview shows connectivity, ReMe version, endpoint, refresh time, automatic-memory and consolidation settings, process RSS, estimated component memory, active sessions, and queued turns. **Server configuration (redacted)** exposes a safe view of `app_config`. A green **Connected** badge confirms the health request, but optional component availability should still be checked under Components.
+
+### 8.2 Auto Memory
+
+
+
+This tab reports active sessions, queued turns, running tasks, queued tasks, and the pipeline from conversation turns through the submission queue to long-term memory. Activity states include **Queued**, **Running**, **Completed**, **Failed**, and **Cancelled**. Activity is process-local diagnostic history; ReMe workspace files remain the durable source of truth.
+
+### 8.3 Memory Consolidation
+
+
+
+This tab shows the next run, cron schedule, timezone, and most recent result. The flow is **Journal entries → Organize and connect → Personal knowledge base**. **Consolidate Memory Now** manually invokes `auto_dream`, which may call a model and modify workspace files.
+
+### 8.4 Components
+
+
+
+Components displays health and resource usage for the file graph, file store, keyword index, and optional embedding store. An unconfigured embedding instance is not itself a failure. If a derived index is unhealthy, rebuild it from source Markdown instead of deleting or rewriting user memory.
+
+### 8.5 Journal
+
+
+
+Journal browses the workspace's `daily` files. The left pane searches and selects files; the right pane previews paths, frontmatter metadata, and Markdown content. The list is capped at the newest 5,000 files.
+
+### 8.6 Personal Knowledge Base
+
+
+
+Personal Knowledge Base browses consolidated `digest` files. Journal entries preserve time-oriented source material, while digest documents hold stable, deduplicated knowledge for long-term recall. Wikilinks can preserve provenance back to the source journal entry.
+
+## 9. Troubleshooting
+
+### ReMe Status reports Unavailable
+
+- Confirm `reme start` is still running and verify the endpoint protocol, host, and port.
+- In containers or cross-machine deployments, `127.0.0.1` refers to each machine separately; configure a browser-reachable address.
+- Verify that ReMe allows the DSH Web origin.
+- Increase `requestTimeoutMs` when the service legitimately needs more than ten seconds.
+
+### No memory context appears
+
+- Create a new session after changing `language`; existing sessions are not reinjected.
+- Subagents are intentionally skipped when `rootAgentsOnly=true`.
+- One session receives the guidance only once, deduplicated by provenance metadata.
+
+### The agent does not call `reme_search`
+
+- Explicitly ask it to use `reme_search`, base the answer on the result, and cite sources.
+- Confirm the selected agent preset allows global tools.
+- Check that the package was loaded through its DSH bundle patch, not merely installed as a dependency.
+
+### Search returns no useful result
+
+- Confirm the source file exists under Journal or Personal Knowledge Base.
+- Use a focused query and adjust `limit` or `min_score` only when needed.
+- Check file store, keyword index, and embedding-store health under Components.
+- Rebuild derived indexes from source files; never rewrite source memory just to satisfy an index.
+
+### A completed chat has not appeared in Journal
+
+- Confirm `autoMemoryEnabled=true` and check whether `autoMemoryInterval` has been reached.
+- Inspect queued, running, and failed states under Auto Memory.
+- Allow for background completion. Shutdown only has the configured `shutdownTimeoutMs` drain budget.
+
+## 10. Validation represented by these screenshots
+
+The test used the DSH `default` workspace and verified:
+
+- DSH UI and ReMe guidance language set to English.
+- English `reme-memory` plugin context with correct provenance.
+- Two real `reme_search` calls returning consistent `daily` and `digest` evidence.
+- Successful background `auto_memory` submission with no queued task remaining.
+- Working Overview, Auto Memory, Memory Consolidation, Components, Journal, and Personal Knowledge Base tabs.
+
+DSH screenshots live in `typescript/figures/dsh/`. Future hosts can use parallel directories such as `typescript/figures/openclaw/`.
diff --git a/typescript/docs/dsh.zh-CN.md b/typescript/docs/dsh.zh-CN.md
new file mode 100644
index 00000000..04ca9245
--- /dev/null
+++ b/typescript/docs/dsh.zh-CN.md
@@ -0,0 +1,281 @@
+# ReMe DeepSeek Harness 插件使用指南
+
+本文介绍如何在 DeepSeek Harness(DSH)中安装、配置和使用 `@agentscope-ai/reme`,并解释插件提供的长期记忆指引、`reme_search` 工具、自动记忆和 ReMe 状态页面。
+
+本文截图来自一次真实的本地联调:DSH 选择 `default` 工作区,界面和 ReMe 指引均设置为 English,ReMe 使用隔离的临时 workspace,示例“Project Polaris”是为演示创建的英文数据。截图不包含 `.env` 内容、API Key 或真实个人记忆。
+
+## 1. 插件做了什么
+
+DSH 启动新会话时,插件向根 Agent 注入一段“如何使用长期记忆”的指引,并注册只读工具 `reme_search`。会话完成后,插件可以把用户与助手的对话按批次交给 ReMe `auto_memory`;每天还可以按计划调用 `auto_dream`,把日记进一步整理成个人知识。
+
+一次典型的数据流如下:
+
+```text
+新会话
+ └─ 注入长期记忆使用指引
+ └─ Agent 判断问题是否依赖历史信息
+ └─ reme_search → ReMe search → daily / digest 文件
+
+完成的对话
+ └─ 自动记忆批次 → ReMe auto_memory → daily 文件
+ └─ 定时记忆整理 → ReMe auto_dream → digest 文件
+```
+
+重要区别:DSH 适配器在普通对话开始时注入的是**记忆使用指引**,不是把所有历史记忆自动塞进上下文。真正与问题相关的记忆由 Agent 调用 `reme_search` 获取。这能减少无关上下文,也避免把历史内容误当成新指令。
+
+## 2. 环境要求
+
+- ReMe Python 服务已安装,且配置中提供 `search`、`auto_memory` 和 `auto_dream` Job。
+- DeepSeek Harness `0.1.2-rc.1` 或更高版本。
+- Node.js `22.22.3+`、`24.15.0+` 或 `25.9.0+` 中的一条受支持版本线。
+- DSH 页面能够访问 ReMe HTTP 地址;跨机器部署时还要允许 DSH 页面所在的浏览器 Origin。
+
+ReMe HTTP 服务默认监听 `http://127.0.0.1:2333`,不使用 API Key 认证。因此不建议未经网络隔离直接暴露到公网。
+
+## 3. 安装与启动
+
+### 3.1 启动 ReMe
+
+```bash
+reme start workspace_dir=/absolute/path/to/your/reme-workspace
+```
+
+开发或截图测试时建议使用仓库外的独立目录,例如 `/tmp/reme-dsh-demo`,不要把运行时记忆写入 ReMe 仓库自身的 `.reme/`。
+
+### 3.2 安装插件
+
+安装已发布版本:
+
+```bash
+dsh plugin --profile web add @agentscope-ai/reme
+```
+
+开发本仓库时,也可以把本地 TypeScript 包链接到 DSH workspace,然后仍按 DSH 的 bundle 协议加载:
+
+```bash
+cd /path/to/deepseek-harness
+pnpm link /path/to/ReMe/typescript --workspace-root
+dsh plugin --profile web add @agentscope-ai/reme
+```
+
+包通过 `package.json` 的 `dsh.bundle.patch` 声明 `dsh/cordis.patch.yml`。该 patch 在独立 `remeMemory` realm 中加载运行时入口 `@agentscope-ai/reme/dsh` 和 Web 客户端入口 `@agentscope-ai/reme`,符合 DSH `0.1.2-rc.1` 的插件协议。
+
+### 3.3 启动 DSH Web
+
+```bash
+cd /path/to/deepseek-harness
+set -a
+source .env
+set +a
+
+dsh web --no-open --port 3080
+```
+
+打开输出中的本地地址,选择工作区 `default`。如果服务启用了访问令牌,使用启动日志给出的地址或按 DSH 提示完成认证;不要把令牌写进文档和截图。
+
+## 4. ReMe Memory 配置
+
+进入 **设置 → 插件 → 插件配置 → ReMe Memory**。修改后点击保存;设置存入 DSH 用户设置文档,并从后续请求或捕获开始生效。修改 `language` 只影响之后创建的新会话,修改每日计划会重新安排下一次整理。
+
+
+
+截图中的测试配置使用 `http://127.0.0.1:2333`、English 指引、默认搜索数量 5、搜索超时 10 秒,并启用了自动记忆和“Exclude subagents”。完整字段如下:
+
+| 界面含义 | 配置键 | 默认值 | 说明 |
+| -------------------- | --------------------- | ----------------------- | -------------------------------------------------------------------------- |
+| 服务地址 | `endpoint` | `http://127.0.0.1:2333` | DSH 访问的 ReMe HTTP 服务绝对地址,只支持 `http`/`https`。 |
+| 记忆指引语言 | `language` | `en` | `en` 或 `zh`;决定新会话中注入的指引语言。 |
+| 默认搜索数量 | `searchLimit` | `5` | `reme_search` 未传 `limit` 时的默认结果上限,范围 1–50。 |
+| 搜索超时(毫秒) | `requestTimeoutMs` | `10000` | `search` 请求的超时,范围 1,000–120,000。 |
+| 自动记录已完成的对话 | `autoMemoryEnabled` | `true` | 是否捕获完成的用户/助手回合并提交给 `auto_memory`。 |
+| 排除子 Agent | `rootAgentsOnly` | `true` | 开启时,只为根 Agent 注入指引并捕获对话。 |
+| 自动记忆提交间隔 | `autoMemoryInterval` | `5` | 每完成多少轮对话提交一次,范围 1–1,000。退出时插件会在时间预算内尽力排空。 |
+| 自动整理记忆 | `autoDreamEnabled` | `true` | 是否按照每日计划调用 `auto_dream`。 |
+| 整理计划 | `dreamCron` | `0 23 * * *` | 五段 cron 表达式,按 `timezone` 解释;默认每天 23:00。 |
+| 整理指引 | `dreamHint` | 空 | 可选的整理提示,原样交给 `auto_dream`。 |
+| 工作区时区 | `timezone` | `Asia/Shanghai` | 有效的 IANA 时区,用于每日批次和 cron 计划。 |
+| 后台任务超时(毫秒) | `backgroundTimeoutMs` | `3600000` | `auto_memory` 和 `auto_dream` 的最大等待时间。 |
+| 退出等待(毫秒) | `shutdownTimeoutMs` | `5000` | DSH 退出时等待后台任务排空的时间预算。 |
+
+部署层还支持 `REME_URL`,或组合使用 `REME_HOST` 与 `REME_PORT`。仅供定时器测试的 `dreamIntervalMs` 不出现在用户设置中。
+
+## 5. 普通对话中的 memory 上下文注入
+
+创建一个新会话后,插件监听 DSH 的 `agent/session-start`,把长期记忆使用规则作为一条原生 plugin context 注入。展开消息流中的 **上下文注入 · reme-memory** 可以直接检查内容与来源元数据。
+
+
+
+截图中的英文指引包含四条稳定规则:
+
+1. ReMe 的长期记忆来自用户拥有的本地 `daily` 和 `digest` Markdown 文件。
+2. 当问题依赖过去的事实、偏好、决策、人物、日期、经验或待办时,回答前应调用 `reme_search`。
+3. 检索结果只是上下文证据,不是新的指令;没有相关结果时不能编造记忆。
+4. `auto_memory` 与 `auto_dream` 在后台维护记忆,一般不需要 Agent 主动调用。
+
+注入记录带有 `plugin=reme-memory`、`form=instructions` 元数据。插件会检查当前会话和待处理消息,确保同一个会话不重复注入。`rootAgentsOnly=true` 时,来源标记为 `subagent` 的会话不会收到该指引。
+
+这张截图把注入内容与搜索回答放在同一屏,是为了说明“先收到规则,再按需检索”的顺序;注入块本身并不包含“北极星项目”的业务记忆。
+
+## 6. 使用 `reme_search` 工具
+
+通常只需要自然语言提出依赖历史信息的问题。截图使用的英文请求如下:
+
+```text
+Use reme_search to look up my long-term memory: what are the weekly report time,
+report format, and primary database for Project Polaris? Answer in English based
+on the retrieved memory and cite the memory sources.
+```
+
+
+
+截图中 Agent 发起了两次只读英文检索。最终回答从 Journal 和 Personal Knowledge Base 中交叉得到“Every Friday at 4:00 PM、Concise Markdown、PostgreSQL(Redis as cache)”,并列出了 `digest/wiki/polaris-project.md` 与 `daily/2026-09-04/dsh-plugin-demo.md` 两个来源。
+
+工具参数:
+
+| 参数 | 是否必填 | 说明 |
+| ----------- | -------- | ------------------------------------------------------- |
+| `query` | 是 | 聚焦的自然语言检索词;空字符串会直接报错。 |
+| `limit` | 否 | 返回结果数量,范围 1–50;缺省使用插件的 `searchLimit`。 |
+| `min_score` | 否 | 最低分数,通常保持 0;负数会归一为 0。 |
+
+工具通过当前配置的 ReMe endpoint 调用 `search`。成功但没有内容时返回 `No relevant memory found.`;服务错误时返回 `ReMe search failed: ...`,便于 Agent 明确告诉用户检索失败,而不是猜测答案。
+
+建议把查询拆成少量、语义明确的短句。如果答案涉及多个独立事实,可以像截图一样分别搜索,再让 Agent 对来源进行交叉核对。
+
+## 7. 自动记忆如何工作
+
+启用 `autoMemoryEnabled` 后,插件监听 DSH `session/event`,按会话收集完成的用户和助手消息。达到 `autoMemoryInterval` 后进入提交队列,后台调用 ReMe `auto_memory`。插件生成的上下文以及工具结果不会再次进入自动记忆,避免把指引或检索回显循环写回长期记忆。
+
+截图测试把间隔临时设为 1;运行记录中可以看到多个英文测试会话形成的已完成提交:
+
+
+
+自动记忆是后台任务:聊天回答完成不代表磁盘写入已经在同一毫秒完成。需要确认时,打开 **ReMe 状态 → 自动记忆**,等待“运行中任务”和“排队任务”归零,并检查最近提交是否为“已完成”。
+
+## 8. ReMe 状态页六个标签
+
+进入 **Settings → ReMe Status**。页面首次打开或主动刷新时读取完整服务诊断;页面可见时,每 5 秒仅刷新 DSH 插件的运行时计数。这里的“标签”指页面顶部的六个 tab:Overview、Auto Memory、Memory Consolidation、Components、Journal、Personal Knowledge Base。
+
+### 8.1 总览
+
+
+
+总览用于快速判断集成是否可用:
+
+- 顶部展示连接状态、ReMe 版本、endpoint 和最近刷新时间。
+- 自动记忆卡显示开关和提交间隔;记忆整理卡显示开关及下一次执行时间。
+- 进程内存是 ReMe 服务 RSS;组件内存是状态接口汇总的组件内存估算。
+- 活跃会话、待处理回合来自当前 DSH 插件运行时,不是历史累计值。
+- “服务配置(已脱敏)”展示 ReMe 返回的 `app_config` 安全视图,敏感字段不会原样显示。
+- “打开 ReMe”访问服务提供的 ReMe 页面;“ReMe 官网”打开项目网站。
+
+绿色“已连接”意味着健康检查成功,不代表每个可选组件都已配置;组件详情应查看“组件”标签。
+
+### 8.2 自动记忆
+
+
+
+该页把会话捕获状态拆成四个计数:活跃会话、待处理回合、运行中任务、排队任务。流程图表示“对话回合 → 提交队列 → 长期记忆”。
+
+状态含义:
+
+- **空闲**:当前没有正在执行或排队的提交;不等于功能关闭。
+- **运行中/排队中**:批次正在请求 ReMe,或等待前面的任务完成。
+- **已完成**:ReMe 接受并完成该批次;行尾显示回合数和消息数。
+- **失败**:请求或 Job 执行失败,页面会显示最近错误。
+- **已取消**:关停期间任务未能在退出预算内完成。
+
+运行记录保存在当前 DSH 进程的内存中,重启 DSH 后会重新计数;真正的长期数据仍以 ReMe workspace 文件为准。
+
+### 8.3 记忆整理
+
+
+
+该页展示下一次整理时间、cron、时区和本次进程中的最近执行结果。流程为“日记记录 → 整理与关联 → 个人知识库”。点击 **立即整理** 会手动发起一次 `auto_dream`,可能调用模型并修改 ReMe workspace,应只在确实需要整理时使用。
+
+cron 按插件配置的 IANA 时区解释。截图中的 `0 23 * * *` 与 `Asia/Shanghai` 表示每天北京时间 23:00。修改计划后无需重启 DSH,插件会重新调度。
+
+### 8.4 组件
+
+
+
+组件页展示 ReMe 的索引与存储基础设施。顶部 `3 / 3` 表示三个已配置的组件均健康;“向量存储未配置实例”是可选能力未启用,不等同于故障。
+
+- **文件图谱**:显示节点、边、虚拟节点、待处理节点和内存占用,用于文件间关系与 wikilink。
+- **文件存储**:显示切分后的内容块、已生成向量的块数和内存占用。
+- **关键词索引**:显示已索引文档、词汇量和内存占用,为本次演示的文本检索提供能力。
+- **向量存储**:若启用,会显示模型、向量维度、缓存条目和内存;未配置时仍可使用服务端已有的其他搜索能力。
+
+如果组件显示“需要处理”或“未启动”,优先查看 ReMe 服务日志和服务端配置,不要删除源 Markdown 文件来修复派生索引。
+
+### 8.5 日记
+
+
+
+日记页浏览 ReMe workspace 的 `daily` 内容。左侧可以搜索和选择文件,右侧显示路径、frontmatter 元数据和 Markdown 正文。截图中除英文手工演示笔记外,还能看到英文搜索对话经 `auto_memory` 生成的条目。
+
+列表最多显示最新 5,000 个文件。此页面用于查看,不改变“workspace 文件是持久事实源”的原则;索引、目录和缓存都应能够从这些文件重建。
+
+### 8.6 个人知识库
+
+
+
+个人知识库页浏览 `digest` 下经过整理的长期知识。布局与日记页一致:左侧文件列表,右侧元数据与内容预览。截图中的 `polaris-project.md` 汇总了长期偏好和技术决策,并通过 wikilink 指回原始日记来源。
+
+日记更接近按天产生的原始记录,个人知识库更适合稳定、去重、可持续召回的知识。`reme_search` 可以同时从服务配置允许的这些来源中检索。
+
+## 9. 日常使用建议
+
+1. 先确认 **ReMe 状态 → 总览** 为“已连接”。
+2. 新建会话后,可在消息详情中展开 `reme-memory` 上下文确认指引已注入。
+3. 问题依赖历史事实时,明确要求 Agent 调用 `reme_search`,并要求列出来源。
+4. 对话结束后,到“自动记忆”确认批次完成,再到“日记”检查落盘内容。
+5. 用“记忆整理”的定时计划做日常归纳;只有需要立即验证时才手动触发。
+6. 定期查看“组件”健康,但修复索引时始终从 workspace 源文件重建,不要反向覆盖用户记忆。
+
+## 10. 常见问题
+
+### 状态页显示“连接失败”
+
+- 确认 `reme start` 仍在运行,并检查 `endpoint` 的协议、主机和端口。
+- DSH 与 ReMe 在容器或不同机器时,`127.0.0.1` 指向各自本机,需要改成浏览器可访问的地址。
+- 检查 ReMe 的 CORS 是否允许 DSH Web 的 Origin。
+- 超过 10 秒才响应时,按实际情况提高 `requestTimeoutMs`。
+
+### 新会话没有看到上下文注入
+
+- 保存 `language` 或开关设置后需要新建会话;已有会话不会补注入或更换语言。
+- 子 Agent 在 `rootAgentsOnly=true` 时会被主动跳过。
+- 同一会话只注入一次,插件会通过来源元数据去重。
+
+### Agent 没有调用 `reme_search`
+
+- 用明确措辞说明问题依赖长期记忆,并要求“使用 `reme_search`、基于结果回答、标明来源”。
+- 检查当前 Agent 预设是否允许全局工具,以及消息详情中是否出现 ReMe 指引。
+- 检查插件是否按 DSH bundle patch 正常加载,而不只是把 npm 包安装到了依赖目录。
+
+### 搜索无结果或结果过多
+
+- 在“日记”和“个人知识库”中确认源文件确实存在。
+- 使用更聚焦的查询,必要时调整 `limit` 或 `min_score`。
+- 检查“组件”中的文件存储、关键词索引或向量存储状态。
+- 索引异常时重建派生状态,不要删除或改写源记忆来迁就索引。
+
+### 聊天完成但日记还没更新
+
+- 确认 `autoMemoryEnabled=true`,并了解 `autoMemoryInterval` 是达到多少个完成回合才提交。
+- 打开“自动记忆”检查待处理、排队、运行和失败状态。
+- 后台 Job 可能比聊天响应稍晚完成;DSH 退出时只有 `shutdownTimeoutMs` 的排空预算。
+
+## 11. 本文联调结果
+
+本次使用 `default` DSH 工作区完成了以下真实链路验证:
+
+- ReMe `0.4.1.11` 服务连接成功。
+- DSH 界面语言与 ReMe `Guidance language` 均已切换并保存为 English。
+- 新会话出现英文 `reme-memory` plugin context,来源元数据正确。
+- Agent 两次调用 `reme_search` 并从英文 `daily`、`digest` 返回一致答案。
+- 英文会话通过 `auto_memory` 完成后台提交,状态页无排队任务。
+- 总览、自动记忆、记忆整理、组件、日记、个人知识库六个标签均能读取并展示数据。
+
+DSH 截图统一放在 `typescript/figures/dsh/`,本文位于 `typescript/docs/`。后续其他宿主的截图可以使用并列目录,例如 `typescript/figures/openclaw/`。运行时演示记忆位于仓库外的临时 workspace,不属于项目产物。
diff --git a/typescript/README_OPENCLAW.md b/typescript/docs/openclaw.md
similarity index 99%
rename from typescript/README_OPENCLAW.md
rename to typescript/docs/openclaw.md
index 8ee632c0..de9a616a 100644
--- a/typescript/README_OPENCLAW.md
+++ b/typescript/docs/openclaw.md
@@ -1,6 +1,6 @@
# ReMe memory for OpenClaw
-[中文说明](./README_ZH.md)
+[中文说明](./openclaw.zh-CN.md)
ReMe gives OpenClaw file-native long-term memory while keeping durable memory in a workspace you own. The plugin uses
OpenClaw's native lifecycle, hooks, tools, and memory slot.
diff --git a/typescript/README_OPENCLAW_ZH.md b/typescript/docs/openclaw.zh-CN.md
similarity index 99%
rename from typescript/README_OPENCLAW_ZH.md
rename to typescript/docs/openclaw.zh-CN.md
index d518b506..01444319 100644
--- a/typescript/README_OPENCLAW_ZH.md
+++ b/typescript/docs/openclaw.zh-CN.md
@@ -1,6 +1,6 @@
# OpenClaw 的 ReMe 长期记忆插件
-[English](./README.md)
+[English](./openclaw.md)
ReMe 为 OpenClaw 提供文件原生的长期记忆,并将持久记忆保存在用户拥有的 workspace 中。插件使用 OpenClaw
原生的生命周期、Hook、工具接口和 memory slot。
diff --git a/typescript/figures/dsh/memory-context-injection.png b/typescript/figures/dsh/memory-context-injection.png
new file mode 100644
index 00000000..c105deb9
Binary files /dev/null and b/typescript/figures/dsh/memory-context-injection.png differ
diff --git a/typescript/figures/dsh/memory-search-tool.png b/typescript/figures/dsh/memory-search-tool.png
new file mode 100644
index 00000000..42b8b114
Binary files /dev/null and b/typescript/figures/dsh/memory-search-tool.png differ
diff --git a/typescript/figures/dsh/reme-memory-settings.png b/typescript/figures/dsh/reme-memory-settings.png
new file mode 100644
index 00000000..fceeeede
Binary files /dev/null and b/typescript/figures/dsh/reme-memory-settings.png differ
diff --git a/typescript/figures/dsh/reme-status-auto-dream.png b/typescript/figures/dsh/reme-status-auto-dream.png
new file mode 100644
index 00000000..d9e197c8
Binary files /dev/null and b/typescript/figures/dsh/reme-status-auto-dream.png differ
diff --git a/typescript/figures/dsh/reme-status-auto-memory.png b/typescript/figures/dsh/reme-status-auto-memory.png
new file mode 100644
index 00000000..0ddb845f
Binary files /dev/null and b/typescript/figures/dsh/reme-status-auto-memory.png differ
diff --git a/typescript/figures/dsh/reme-status-components.png b/typescript/figures/dsh/reme-status-components.png
new file mode 100644
index 00000000..0ee26116
Binary files /dev/null and b/typescript/figures/dsh/reme-status-components.png differ
diff --git a/typescript/figures/dsh/reme-status-journal.png b/typescript/figures/dsh/reme-status-journal.png
new file mode 100644
index 00000000..2775ab79
Binary files /dev/null and b/typescript/figures/dsh/reme-status-journal.png differ
diff --git a/typescript/figures/dsh/reme-status-knowledge.png b/typescript/figures/dsh/reme-status-knowledge.png
new file mode 100644
index 00000000..d219c852
Binary files /dev/null and b/typescript/figures/dsh/reme-status-knowledge.png differ
diff --git a/typescript/figures/dsh/reme-status-overview.png b/typescript/figures/dsh/reme-status-overview.png
new file mode 100644
index 00000000..df0dcf7b
Binary files /dev/null and b/typescript/figures/dsh/reme-status-overview.png differ
diff --git a/typescript/package-lock.json b/typescript/package-lock.json
index 911e3f93..a36cc88b 100644
--- a/typescript/package-lock.json
+++ b/typescript/package-lock.json
@@ -12,13 +12,13 @@
"typebox": "1.3.19"
},
"devDependencies": {
- "@deepseek-ai/cordis": "4.0.1",
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.0-rc.8",
- "@deepseek-ai/dsh-llm": "0.1.0-rc.8",
- "@deepseek-ai/dsh-settings": "0.1.0-rc.8",
- "@deepseek-ai/dsh-tools": "0.1.0-rc.8",
- "@deepseek-ai/dsh-typert-protocol": "0.1.0-rc.8",
- "@deepseek-ai/schemastery": "3.18.1",
+ "@deepseek-ai/cordis": "4.0.2",
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-rc.1",
+ "@deepseek-ai/dsh-llm": "0.1.2-rc.1",
+ "@deepseek-ai/dsh-settings": "0.1.2-rc.1",
+ "@deepseek-ai/dsh-tools": "0.1.2-rc.1",
+ "@deepseek-ai/dsh-typert-protocol": "0.1.2-rc.1",
+ "@deepseek-ai/schemastery": "3.18.2",
"@eslint/js": "9.39.4",
"@types/node": "^22.15.0",
"@types/react": "~18.3.1",
@@ -36,13 +36,13 @@
"node": "^22.22.3 || ^24.15.0 || >=25.9.0"
},
"peerDependencies": {
- "@deepseek-ai/cordis": "^4.0.1",
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.8",
- "@deepseek-ai/schemastery": "^3.18.1",
+ "@deepseek-ai/cordis": "^4.0.2",
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-settings": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-tools": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.2-rc.1",
+ "@deepseek-ai/schemastery": "^3.18.2",
"openclaw": ">=2026.7.1"
},
"peerDependenciesMeta": {
@@ -313,21 +313,21 @@
}
},
"node_modules/@deepseek-ai/cordis": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/@deepseek-ai/cordis/-/cordis-4.0.1.tgz",
- "integrity": "sha512-YBdskTU2Po1kru3GgcUWUbkTsPMA9LkSQDAY8rBkFJeajdgcQad3QPJZE26JyK99Xb6HaASvoXg2DSUTeN/0Nw==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/cordis/-/cordis-4.0.2.tgz",
+ "integrity": "sha512-asOnXP1TzFSFQlHb1iegDZp0z/8WD1c7YNrwJR/Tx2bzNuMXfcekE/I67Iv6SQXeLB4csxqCngzQKANP7gdw0g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@deepseek-ai/cosmokit": "^1.8.2",
+ "@deepseek-ai/cosmokit": "^1.8.3",
"@standard-schema/spec": "^1.1.0"
},
"bin": {
"cordis": "bin.js"
},
"peerDependencies": {
- "@deepseek-ai/cordis-plugin-include": "^1.0.6",
- "@deepseek-ai/cordis-plugin-loader": "^1.0.2"
+ "@deepseek-ai/cordis-plugin-include": "^1.0.7",
+ "@deepseek-ai/cordis-plugin-loader": "^1.0.3"
},
"peerDependenciesMeta": {
"@deepseek-ai/cordis-plugin-include": {
@@ -339,58 +339,44 @@
}
},
"node_modules/@deepseek-ai/cosmokit": {
- "version": "1.8.2",
- "resolved": "https://registry.npmjs.org/@deepseek-ai/cosmokit/-/cosmokit-1.8.2.tgz",
- "integrity": "sha512-muBOKtSrUKU5m/xpq8ZXWL6hQ/jgd4PhU2PqH97bcxIiLEJfNwZOGQEx4t/aS/GgxRAR+ra9pMHPMtTHU4sqqA==",
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/cosmokit/-/cosmokit-1.8.3.tgz",
+ "integrity": "sha512-qBo+ronVM6Eu2WNVJXi8JcMiqZ19T9BRIpV+5qJUFPXjGH/Z0QKcQMC/IZJ7L394YTOtJgcovbk9qP0w2GsBXQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@deepseek-ai/dsh-agent": {
- "version": "0.1.0-rc.8",
- "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-agent/-/dsh-agent-0.1.0-rc.8.tgz",
- "integrity": "sha512-fOl46ZvzoYHalrlyfOJAHNhzyKeVDTUnZZKNSJEr9GY/98WJTjcV21agVEofTXldkJ7a/ws2HpJkVNJVFMtslg==",
+ "version": "0.1.2-rc.1",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-agent/-/dsh-agent-0.1.2-rc.1.tgz",
+ "integrity": "sha512-lfaqN34vUCWvbn1kJVHrhfJ6Dvt1HDHCm33ZCpmKkl07/5q6FxWqVv6rOdVX5QnM/9xz/uYiN0nQwUtBg4+Skg==",
"dev": true,
"license": "MIT",
"peer": true,
"peerDependencies": {
- "@deepseek-ai/cordis": "^4.0.1",
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-scope": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-session": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.8"
- }
- },
- "node_modules/@deepseek-ai/dsh-attachment": {
- "version": "0.1.0-rc.8",
- "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-attachment/-/dsh-attachment-0.1.0-rc.8.tgz",
- "integrity": "sha512-cCrg4WWiav7pGtbdU8dJTpAG28cPnkWVB2YPOrlBCgBbhQziGcG9Dv67Ti6L3PI4OQKC55gYBFinQghFO/7FGg==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "peerDependencies": {
- "@deepseek-ai/cordis": "^4.0.1",
- "@deepseek-ai/dsh-brand": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
+ "@deepseek-ai/cordis": "^4.0.2",
+ "@deepseek-ai/dsh-invariants": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-scope": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-session": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-session-projection": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-system-prompt": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.2-rc.1"
}
},
"node_modules/@deepseek-ai/dsh-brand": {
- "version": "0.1.0-rc.8",
- "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-brand/-/dsh-brand-0.1.0-rc.8.tgz",
- "integrity": "sha512-402aUAfHxIZJrArBV4gDf0I/A/69A/By26FX6HTIOyFmCnB4wUBKh8jnglz1CorKWdZy8AKldAhh8HQhQEbGOQ==",
+ "version": "0.1.2-rc.1",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-brand/-/dsh-brand-0.1.2-rc.1.tgz",
+ "integrity": "sha512-y2RHIag3kzLdY8kTBuSUSTsKzWPscJrysNObXTiNM7stXLpa3jXpshsB1caqGzNx50vYcmHeN1gFVECgx3NZHg==",
"dev": true,
"license": "MIT",
- "peer": true,
"peerDependencies": {
- "@deepseek-ai/cordis": "^4.0.1",
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
+ "@deepseek-ai/cordis": "^4.0.2"
}
},
"node_modules/@deepseek-ai/dsh-client-ui-primitives": {
- "version": "0.1.0-rc.8",
- "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-client-ui-primitives/-/dsh-client-ui-primitives-0.1.0-rc.8.tgz",
- "integrity": "sha512-P3/n4jWT5kp4kTH67h+nmLu8LhKl9KsbFn8plumQFPXKGj2ythwmVoK3Nc329ANcYlxKXVtnoLPwYrTxkSeIpA==",
+ "version": "0.1.2-rc.1",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-client-ui-primitives/-/dsh-client-ui-primitives-0.1.2-rc.1.tgz",
+ "integrity": "sha512-4gZS+tXAHbSxAjP91+lENpxKb49XVKcO80m7ATNNgD6lJHOwbuWVGv9IwEnmQ5ebZvI8Yrp8C1J+jDe1pB9jXw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -416,184 +402,223 @@
"shiki": "^4.3.1"
},
"peerDependencies": {
- "@deepseek-ai/cordis": "^4.0.1",
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
+ "@deepseek-ai/cordis": "^4.0.2"
}
},
"node_modules/@deepseek-ai/dsh-code-runtime": {
- "version": "0.1.0-rc.8",
- "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-code-runtime/-/dsh-code-runtime-0.1.0-rc.8.tgz",
- "integrity": "sha512-6ZKyEE7dHH1UkKe5b0lg18Byww/7ocZyrNMVjAoUWEWp5i96Brf9Eavu9q3zepBMDMZJErCfV0pyt7KDhBJSig==",
+ "version": "0.1.2-rc.1",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-code-runtime/-/dsh-code-runtime-0.1.2-rc.1.tgz",
+ "integrity": "sha512-dv5UszPJb/AFcarZc2c6h5z/X80bsrSd3bfIOugwNVclSI6P+L3ixseoZKgyks7T0Ap0h6ngXGk2WeoUkmYxnQ==",
"dev": true,
"license": "MIT",
"peer": true,
"peerDependencies": {
- "@deepseek-ai/cordis": "^4.0.1",
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
+ "@deepseek-ai/cordis": "^4.0.2"
}
},
"node_modules/@deepseek-ai/dsh-invariants": {
- "version": "0.1.0-rc.8",
- "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-invariants/-/dsh-invariants-0.1.0-rc.8.tgz",
- "integrity": "sha512-u0lYqyxOYwfsVnbsfGXZos5vFvA4cqFnBEW3/ezgljNwkYwzeUP/Y5wjPnQjP+ZzBn3CnVeIF6s2N2Vk3iA5mQ==",
+ "version": "0.1.2-rc.1",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-invariants/-/dsh-invariants-0.1.2-rc.1.tgz",
+ "integrity": "sha512-zSjuFJYcEB94kPwn7SicqAV1dfVWZkjt1I8luDboxtjfp5CN6ziwJhLv3KaB+Dh9tgkyGY4mvo886W/HhE9L9g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
- "@deepseek-ai/schemastery": "^3.18.1"
+ "@deepseek-ai/schemastery": "^3.18.2"
},
"peerDependencies": {
- "@deepseek-ai/cordis": "^4.0.1"
+ "@deepseek-ai/cordis": "^4.0.2"
}
},
"node_modules/@deepseek-ai/dsh-llm": {
- "version": "0.1.0-rc.8",
- "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-llm/-/dsh-llm-0.1.0-rc.8.tgz",
- "integrity": "sha512-hf6RmX2Stn1UtzpktSHQLkIktQkBm9i/3yFfJlbGSkwyw5Cv9sTYJbyoHzsSnnh6i8nRRcMcBhvMgm23I00oIw==",
+ "version": "0.1.2-rc.1",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-llm/-/dsh-llm-0.1.2-rc.1.tgz",
+ "integrity": "sha512-7VYsha5AXsVLnsAwYJffWXz9bwUbElw8i5N8tlTSdai9Bupk3sMbsotzPf8ZbsuGAxQYErahMwQwgGEu4qZO6g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@deepseek-ai/schemastery": "^3.18.1"
+ "@deepseek-ai/dsh-brand": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-timeout": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-util-crypto": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-util-values": "^0.1.2-rc.1",
+ "@deepseek-ai/schemastery": "^3.18.2",
+ "zod": "^4.4.3"
},
"peerDependencies": {
- "@deepseek-ai/cordis": "^4.0.1",
- "@deepseek-ai/dsh-attachment": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-brand": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-timeout": "^0.1.0-rc.8"
+ "@deepseek-ai/cordis": "^4.0.2"
}
},
"node_modules/@deepseek-ai/dsh-scope": {
- "version": "0.1.0-rc.8",
- "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-scope/-/dsh-scope-0.1.0-rc.8.tgz",
- "integrity": "sha512-/o+PxkVV2zUy9lYlMFr4TWSb+ESW4DE+fI891yIbsLzOJdFgfbmmLpGrlWgnz8/unIGJZxjeXdwyppV04Z277Q==",
+ "version": "0.1.2-rc.1",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-scope/-/dsh-scope-0.1.2-rc.1.tgz",
+ "integrity": "sha512-GHwbzWrGbmE0ZGzAZsXBudpyCM9ENCpXxlKODIaPFbIWhRePh6nH2jW47TAOqCIsPPedWC2p3w8Tx5WAa3I/2A==",
"dev": true,
"license": "MIT",
"peer": true,
"peerDependencies": {
- "@deepseek-ai/cordis": "^4.0.1",
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
+ "@deepseek-ai/cordis": "^4.0.2",
+ "@deepseek-ai/dsh-invariants": "^0.1.2-rc.1"
}
},
"node_modules/@deepseek-ai/dsh-session": {
- "version": "0.1.0-rc.8",
- "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-session/-/dsh-session-0.1.0-rc.8.tgz",
- "integrity": "sha512-Qs5Mgn32WPPVB4rKHjsZBgLOkh0TQurUAcc9GaDVudK4dUkpno2HDh++qu/PrwRA3CZ/iwPAoFHCpB3AMMaoLg==",
+ "version": "0.1.2-rc.1",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-session/-/dsh-session-0.1.2-rc.1.tgz",
+ "integrity": "sha512-jRGNPTbQcvIx1F2MVmmkoiHLgpC3Btqo1wkl/3JDLlOWRVWKOuaC+5fQiMrFPOYfno6YiX/c2UvmA0td8qB92w==",
"dev": true,
"license": "MIT",
"peer": true,
+ "dependencies": {
+ "@deepseek-ai/dsh-brand": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-util-values": "^0.1.2-rc.1"
+ },
"peerDependencies": {
- "@deepseek-ai/cordis": "^4.0.1",
- "@deepseek-ai/dsh-brand": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-scope": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.8"
+ "@deepseek-ai/cordis": "^4.0.2",
+ "@deepseek-ai/dsh-scope": "^0.1.2-rc.1"
+ }
+ },
+ "node_modules/@deepseek-ai/dsh-session-projection": {
+ "version": "0.1.2-rc.1",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-session-projection/-/dsh-session-projection-0.1.2-rc.1.tgz",
+ "integrity": "sha512-qVWlIzPm4u9vzQ5G/QtE6jeSJ3b3b42hWVJj664BurEZlHbAfdqkhYc1QsoBU+5k5kUEJFHh07z8HxuXA9kTnQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "zod": "^4.4.3"
+ },
+ "peerDependencies": {
+ "@deepseek-ai/cordis": "^4.0.2",
+ "@deepseek-ai/dsh-session": "^0.1.2-rc.1"
}
},
"node_modules/@deepseek-ai/dsh-settings": {
- "version": "0.1.0-rc.8",
- "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-settings/-/dsh-settings-0.1.0-rc.8.tgz",
- "integrity": "sha512-pqbMoiSP+Ieww9/4gcHYXcBCDlno1zYDu2OPq+5xdZ+McnK+5YBtHsiRpnR9cHQxGTH7lkfIv+adkOueQW0utg==",
+ "version": "0.1.2-rc.1",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-settings/-/dsh-settings-0.1.2-rc.1.tgz",
+ "integrity": "sha512-wMmJ2w5S6I7hgmgPppuAVemltEcTmAa8gvii7fk4T2KGVCuBYgig8xPwQ8Lp2ukLZSzzE6ZVCQxhdYZyJX/USA==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "@deepseek-ai/dsh-util-values": "^0.1.2-rc.1"
+ },
"peerDependencies": {
- "@deepseek-ai/cordis": "^4.0.1",
- "@deepseek-ai/dsh-brand": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
- "@deepseek-ai/schemastery": "^3.18.1"
+ "@deepseek-ai/cordis": "^4.0.2",
+ "@deepseek-ai/dsh-brand": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-invariants": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-session": "^0.1.2-rc.1",
+ "@deepseek-ai/schemastery": "^3.18.2"
}
},
"node_modules/@deepseek-ai/dsh-system-prompt": {
- "version": "0.1.0-rc.8",
- "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-system-prompt/-/dsh-system-prompt-0.1.0-rc.8.tgz",
- "integrity": "sha512-yk8Z85rzX2EwpKNWA5i+K3I3mqsIIUc9niwbDpXO8vlHvpC2qR3RNHjJIJN2EZYDqfSzTVtkbxHFsuMzDXIbew==",
+ "version": "0.1.2-rc.1",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-system-prompt/-/dsh-system-prompt-0.1.2-rc.1.tgz",
+ "integrity": "sha512-7W93PZKIk4CHvjZGgLIxrrEKpk+6t8nQXje1vKR5vG73dfXH0t1MJ5WWH/fngadYc7c6KzM84ihQ66tJqJqyNQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
- "@deepseek-ai/schemastery": "^3.18.1"
+ "@deepseek-ai/schemastery": "^3.18.2"
},
"peerDependencies": {
- "@deepseek-ai/cordis": "^4.0.1",
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-scope": "^0.1.0-rc.8"
+ "@deepseek-ai/cordis": "^4.0.2",
+ "@deepseek-ai/dsh-invariants": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-scope": "^0.1.2-rc.1"
}
},
"node_modules/@deepseek-ai/dsh-timeout": {
- "version": "0.1.0-rc.8",
- "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-timeout/-/dsh-timeout-0.1.0-rc.8.tgz",
- "integrity": "sha512-RwOC/qHribE6b+LStAO7aAgweefLE5sqDO7Uz6/mnTDyJxCt7HNQnE/Op8sAvRa33d752aUkPfO9S0mgzVAoFQ==",
+ "version": "0.1.2-rc.1",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-timeout/-/dsh-timeout-0.1.2-rc.1.tgz",
+ "integrity": "sha512-llz11yxWeJB8suKCa6hRLjNBWVaPn3Fbd0XoBS0bkZLhPFzO1mDorx/XMca2GhI7XF9A86CURQcDt+aGjjvqJA==",
"dev": true,
"license": "MIT",
- "peer": true,
"peerDependencies": {
- "@deepseek-ai/cordis": "^4.0.1",
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
+ "@deepseek-ai/cordis": "^4.0.2"
}
},
"node_modules/@deepseek-ai/dsh-tools": {
- "version": "0.1.0-rc.8",
- "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-tools/-/dsh-tools-0.1.0-rc.8.tgz",
- "integrity": "sha512-tvuGAVUS4yoPW9zH7CGWJgMlAzGjfu2UlRiricldlcwSKAaJZPZx/K/YfTivUyDTER6aK1DJUnIpVG7RD/DoHQ==",
+ "version": "0.1.2-rc.1",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-tools/-/dsh-tools-0.1.2-rc.1.tgz",
+ "integrity": "sha512-W9kUio00s7WbM8kEwniyd4hfb3CeUxVczsjXbOcKtakfiTywNLeyRWkpx2fvwRAH2rBfg3qCgczVfS0DOw1csg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@deepseek-ai/schemastery": "^3.18.1"
+ "@deepseek-ai/dsh-brand": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-util-values": "^0.1.2-rc.1",
+ "@deepseek-ai/schemastery": "^3.18.2"
},
"peerDependencies": {
- "@deepseek-ai/cordis": "^4.0.1",
- "@deepseek-ai/dsh-agent": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-code-runtime": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-scope": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-session": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-user-approval": "^0.1.0-rc.8"
+ "@deepseek-ai/cordis": "^4.0.2",
+ "@deepseek-ai/dsh-agent": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-code-runtime": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-invariants": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-scope": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-session": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-system-prompt": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-user-approval": "^0.1.2-rc.1"
}
},
"node_modules/@deepseek-ai/dsh-typert-protocol": {
- "version": "0.1.0-rc.8",
- "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-typert-protocol/-/dsh-typert-protocol-0.1.0-rc.8.tgz",
- "integrity": "sha512-scwEAefRBL7gRpaXiEdiHRMvlr8dLmUbiEvy0LOAX/8X3FZBd/5tHQ/8lCppAQNTDZMI/LSiSMpu5XrFId+7cQ==",
+ "version": "0.1.2-rc.1",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-typert-protocol/-/dsh-typert-protocol-0.1.2-rc.1.tgz",
+ "integrity": "sha512-vBWD0NpriPd96ltbWaAw+iKeWFIEjiAcCWYtmqEcV1Ms+FaV02usjjm7LCmhSQhMZpBSBc7EZUX0spGlpjI1bA==",
"dev": true,
"license": "MIT",
"peerDependencies": {
- "@deepseek-ai/cordis": "^4.0.1",
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
+ "@deepseek-ai/cordis": "^4.0.2"
}
},
"node_modules/@deepseek-ai/dsh-user-approval": {
- "version": "0.1.0-rc.8",
- "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-user-approval/-/dsh-user-approval-0.1.0-rc.8.tgz",
- "integrity": "sha512-6SFCxp7dgNFEyzQIcs5i9C+5djgXsAeV0iKmZUCLlcIeVM1chcCMHyGcoXlw8yyB14qFMrMs+TPvhvpu2WWxAw==",
+ "version": "0.1.2-rc.1",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-user-approval/-/dsh-user-approval-0.1.2-rc.1.tgz",
+ "integrity": "sha512-oYkLE4a/TwqVNi299pUf/QP1Yku6KScdUCTUyYbjaRBcN+/pXPpHikl2aoE2thH9D0uT/Kj6T2kz+wDDcFd9Xg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
- "@deepseek-ai/schemastery": "^3.18.1"
+ "@deepseek-ai/schemastery": "^3.18.2"
},
"peerDependencies": {
- "@deepseek-ai/cordis": "^4.0.1",
- "@deepseek-ai/dsh-agent": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-brand": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-scope": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-session": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.8"
+ "@deepseek-ai/cordis": "^4.0.2",
+ "@deepseek-ai/dsh-agent": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-brand": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-invariants": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-scope": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-session": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-system-prompt": "^0.1.2-rc.1"
+ }
+ },
+ "node_modules/@deepseek-ai/dsh-util-crypto": {
+ "version": "0.1.2-rc.1",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-util-crypto/-/dsh-util-crypto-0.1.2-rc.1.tgz",
+ "integrity": "sha512-gE8FznQ1hkknw9QMrHNSlm3UEyRYuj3MLVvlJ6NyH1kmOsdjnbrVbSs+Pn72k2oIV4Fiyu3Umg0sLznUEcsiXw==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@deepseek-ai/cordis": "^4.0.2"
+ }
+ },
+ "node_modules/@deepseek-ai/dsh-util-values": {
+ "version": "0.1.2-rc.1",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/dsh-util-values/-/dsh-util-values-0.1.2-rc.1.tgz",
+ "integrity": "sha512-FQdgoK+KYVR0pBQb9fTsajvGb3gzUxEzJQEYnFOt+Qdwc4nkxJOPwJJdIvXuqvZaQ33DaOoqpnpcr2p5HpLu4g==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@deepseek-ai/cordis": "^4.0.2"
}
},
"node_modules/@deepseek-ai/schemastery": {
- "version": "3.18.1",
- "resolved": "https://registry.npmjs.org/@deepseek-ai/schemastery/-/schemastery-3.18.1.tgz",
- "integrity": "sha512-Qn0FCSwCQnpnj6SB31I6i2sIKgKWnkbJM8O0EU91Gv2UsYVvtZTl6IA0sCwk2e2MZf5S8w5hpq9QkeVvK9qwxg==",
+ "version": "3.18.2",
+ "resolved": "https://registry.npmjs.org/@deepseek-ai/schemastery/-/schemastery-3.18.2.tgz",
+ "integrity": "sha512-njDtZsznjYxok7KLLlHOPyuv2efdWVbSflAHgztSfbMsg+CVraEoRe2DjOCgClYv3ZCSm7WXoaUkbB/+RY7tWQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@deepseek-ai/cosmokit": "^1.8.2",
+ "@deepseek-ai/cosmokit": "^1.8.3",
"@standard-schema/spec": "^1.1.0"
}
},
diff --git a/typescript/package.json b/typescript/package.json
index e5093de9..c64572ae 100644
--- a/typescript/package.json
+++ b/typescript/package.json
@@ -28,13 +28,15 @@
"dsh/cordis.patch.yml",
"openclaw.plugin.json",
"README.md",
- "README_ZH.md"
+ "README_ZH.md",
+ "docs",
+ "figures/dsh"
],
"dsh": {
"client": {
"inject": [
"@deepseek-ai/dsh-client-locale",
- "@deepseek-ai/dsh-client-runtime",
+ "@deepseek-ai/dsh-client-connection",
"@deepseek-ai/dsh-client-ui-settings",
"@deepseek-ai/dsh-client-ui-settings-plugins",
"@deepseek-ai/dsh-client-ui-primitives"
@@ -75,13 +77,13 @@
"typebox": "1.3.19"
},
"peerDependencies": {
- "@deepseek-ai/cordis": "^4.0.1",
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.8",
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.8",
- "@deepseek-ai/schemastery": "^3.18.1",
+ "@deepseek-ai/cordis": "^4.0.2",
+ "@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-settings": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.2-rc.1",
+ "@deepseek-ai/dsh-tools": "^0.1.2-rc.1",
+ "@deepseek-ai/schemastery": "^3.18.2",
"openclaw": ">=2026.7.1"
},
"peerDependenciesMeta": {
@@ -111,13 +113,13 @@
}
},
"devDependencies": {
- "@deepseek-ai/cordis": "4.0.1",
- "@deepseek-ai/dsh-llm": "0.1.0-rc.8",
- "@deepseek-ai/dsh-settings": "0.1.0-rc.8",
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.0-rc.8",
- "@deepseek-ai/dsh-typert-protocol": "0.1.0-rc.8",
- "@deepseek-ai/dsh-tools": "0.1.0-rc.8",
- "@deepseek-ai/schemastery": "3.18.1",
+ "@deepseek-ai/cordis": "4.0.2",
+ "@deepseek-ai/dsh-llm": "0.1.2-rc.1",
+ "@deepseek-ai/dsh-settings": "0.1.2-rc.1",
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-rc.1",
+ "@deepseek-ai/dsh-typert-protocol": "0.1.2-rc.1",
+ "@deepseek-ai/dsh-tools": "0.1.2-rc.1",
+ "@deepseek-ai/schemastery": "3.18.2",
"@eslint/js": "9.39.4",
"@types/node": "^22.15.0",
"@types/react": "~18.3.1",
diff --git a/typescript/scripts/pack-clawhub.mjs b/typescript/scripts/pack-clawhub.mjs
index d42e8970..415fc063 100644
--- a/typescript/scripts/pack-clawhub.mjs
+++ b/typescript/scripts/pack-clawhub.mjs
@@ -21,6 +21,14 @@ const temporaryDirectory = await mkdtemp(
);
const stagingDirectory = path.join(temporaryDirectory, "package");
+async function stageReadme(source, destination, oldLink, newLink) {
+ const content = await readFile(path.join(packageDirectory, source), "utf8");
+ await writeFile(
+ path.join(stagingDirectory, destination),
+ content.replace(oldLink, newLink),
+ );
+}
+
try {
await mkdir(stagingDirectory, { recursive: true });
await mkdir(destinationDirectory, { recursive: true });
@@ -49,13 +57,17 @@ try {
path.join(packageDirectory, "openclaw.plugin.json"),
path.join(stagingDirectory, "openclaw.plugin.json"),
),
- copyFile(
- path.join(packageDirectory, "README_OPENCLAW.md"),
- path.join(stagingDirectory, "README.md"),
+ stageReadme(
+ "docs/openclaw.md",
+ "README.md",
+ "(./openclaw.zh-CN.md)",
+ "(./README_ZH.md)",
),
- copyFile(
- path.join(packageDirectory, "README_OPENCLAW_ZH.md"),
- path.join(stagingDirectory, "README_ZH.md"),
+ stageReadme(
+ "docs/openclaw.zh-CN.md",
+ "README_ZH.md",
+ "(./openclaw.md)",
+ "(./README.md)",
),
]);
diff --git a/typescript/scripts/test-package.mjs b/typescript/scripts/test-package.mjs
index 3a9e7654..c976241c 100644
--- a/typescript/scripts/test-package.mjs
+++ b/typescript/scripts/test-package.mjs
@@ -56,6 +56,9 @@ try {
sourceManifest.version,
)});`,
'assert.match(await readFile(new URL("README_ZH.md", manifestUrl), "utf8"), /TypeScript Agent/);',
+ 'assert.match(await readFile(new URL("docs/dsh.md", manifestUrl), "utf8"), /Memory context injection/);',
+ 'assert.match(await readFile(new URL("docs/openclaw.md", manifestUrl), "utf8"), /OpenClaw/);',
+ 'assert.ok((await readFile(new URL("figures/dsh/reme-status-overview.png", manifestUrl))).length > 0);',
].join("\n"),
);
await execFileAsync("node", [consumerEntry], { cwd: temporaryDirectory });
@@ -101,8 +104,10 @@ try {
);
assert.match(clawHubReadme, /^# ReMe memory for OpenClaw/m);
assert.doesNotMatch(clawHubReadme, /DeepSeek Harness/);
+ assert.match(clawHubReadme, /\[中文说明\]\(\.\/README_ZH\.md\)/);
assert.match(clawHubReadmeZh, /^# OpenClaw 的 ReMe 长期记忆插件/m);
assert.doesNotMatch(clawHubReadmeZh, /DeepSeek Harness/);
+ assert.match(clawHubReadmeZh, /\[English\]\(\.\/README\.md\)/);
assert.equal(
JSON.parse(
await readFile(
diff --git a/typescript/src/dsh/client/status-page.tsx b/typescript/src/dsh/client/status-page.tsx
index 62958648..8aa70e92 100644
--- a/typescript/src/dsh/client/status-page.tsx
+++ b/typescript/src/dsh/client/status-page.tsx
@@ -121,6 +121,9 @@ export const statusEn = {
documentContent: "Document content",
selectFile: "Select a file to preview its contents.",
fileLoadFailed: "Could not load this file.",
+ copyCode: "Copy",
+ copiedCode: "Copied",
+ footnotes: "Footnotes",
journalDescription: "Daily notes captured from conversations and sources.",
knowledgeDescription:
"Consolidated knowledge organized for long-term recall.",
@@ -229,6 +232,9 @@ export const statusZh: typeof statusEn = {
documentContent: "文档内容",
selectFile: "选择一个文件查看内容。",
fileLoadFailed: "无法读取这个文件。",
+ copyCode: "复制",
+ copiedCode: "已复制",
+ footnotes: "脚注",
journalDescription: "从对话和其他来源自动沉淀的每日记录。",
knowledgeDescription: "经过整理、适合长期检索和持续积累的个人知识。",
};
@@ -929,6 +935,13 @@ function MemoryFiles({
directory: string;
t: StatusTranslator;
}): JSX.Element {
+ const markdownLabels = useMemo(
+ () => ({
+ code: { copyLabel: t("copyCode"), copiedLabel: t("copiedCode") },
+ footnotes: t("footnotes"),
+ }),
+ [t],
+ );
const [files, setFiles] = useState([]);
const [limited, setLimited] = useState(false);
const [loading, setLoading] = useState(true);
@@ -1084,7 +1097,7 @@ function MemoryFiles({
{t("documentContent")}
-
+
diff --git a/typescript/src/dsh/config.ts b/typescript/src/dsh/config.ts
index 76f55823..addd077a 100644
--- a/typescript/src/dsh/config.ts
+++ b/typescript/src/dsh/config.ts
@@ -1,11 +1,10 @@
import z from "@deepseek-ai/schemastery";
-import { settingsNamespace } from "@deepseek-ai/dsh-settings";
import { nextDailyRun, validTimezone } from "../core/scheduling.js";
import type { ReMeConfig, ReMeConfigInput, ReMeSettings } from "./types.js";
/** Durable DSH settings section owned by the ReMe integration. */
-export const REME_SETTINGS_NAMESPACE = settingsNamespace("reme-memory");
+export const REME_SETTINGS_NAMESPACE = "reme-memory";
export const Config = z.object({
endpoint: z.string().description("ReMe HTTP service URL"),
diff --git a/typescript/src/dsh/index.ts b/typescript/src/dsh/index.ts
index 6a08fb34..a7333ad0 100644
--- a/typescript/src/dsh/index.ts
+++ b/typescript/src/dsh/index.ts
@@ -1,6 +1,6 @@
import { createUserMessage } from "@deepseek-ai/dsh-llm";
import type { Context } from "@deepseek-ai/cordis";
-import { installSettingsSection } from "@deepseek-ai/dsh-settings";
+import type {} from "@deepseek-ai/dsh-settings";
import { ReMeClient } from "../core/client.js";
import {
@@ -22,25 +22,26 @@ export const inject = ["agents", "sessions", "tools"];
export function apply(ctx: Context, input: ReMeConfigInput = {}): void {
const base = resolveConfig(input);
- let settingsSource: () => ReMeSettings = () => settingsFrom(base);
+ const defaultSettings = settingsFrom(base);
+ let settingsSource: () => ReMeSettings = () => defaultSettings;
const current = () => mergeSettings(base, settingsSource());
const client = new ReMeClient(current);
const runtime = new ReMeRuntime(client, current, ctx.logger);
- installSettingsSection(
- ctx,
- REME_SETTINGS_NAMESPACE,
- SettingsConfig,
- settingsFrom(base),
- {
- setSource: (source) => {
- settingsSource = source;
+ ctx.inject(["settings"], (settingsCtx) => {
+ settingsCtx.settings.installSection(
+ ctx,
+ REME_SETTINGS_NAMESPACE,
+ SettingsConfig,
+ defaultSettings,
+ {
+ setSource(source) {
+ settingsSource = source;
+ },
+ onChange: () => runtime.reconfigure(),
+ validate: validateSettings,
},
- onChange: () => {
- runtime.reconfigure();
- },
- validate: validateSettings,
- },
- );
+ );
+ });
ctx.provide("remeMemory", runtime);
void ctx.plugin(ReMeStatusGateway);
ctx.effect(
diff --git a/typescript/tests/bundle.test.mjs b/typescript/tests/bundle.test.mjs
index 0b9a9387..7f1216e3 100644
--- a/typescript/tests/bundle.test.mjs
+++ b/typescript/tests/bundle.test.mjs
@@ -32,15 +32,21 @@ test("declares DSH and OpenClaw entries in one installable package", async () =>
"@deepseek-ai/dsh-client-ui-primitives",
),
);
+ assert.ok(
+ manifest.dsh.client.inject.includes("@deepseek-ai/dsh-client-connection"),
+ );
+ assert.ok(
+ !manifest.dsh.client.inject.includes("@deepseek-ai/dsh-client-runtime"),
+ );
assert.equal(manifest.dsh.bundle.patch, "./dsh/cordis.patch.yml");
assert.deepEqual(manifest.openclaw.extensions, ["./dist/openclaw/index.js"]);
assert.equal(
manifest.peerDependencies["@deepseek-ai/dsh-llm"],
- "^0.1.0-rc.8",
+ "^0.1.2-rc.1",
);
assert.equal(
manifest.peerDependencies["@deepseek-ai/dsh-tools"],
- "^0.1.0-rc.8",
+ "^0.1.2-rc.1",
);
assert.match(patch, /remeMemory: true/);
assert.match(patch, /@agentscope-ai\/reme\/dsh/);
diff --git a/typescript/tests/dsh-index.test.mjs b/typescript/tests/dsh-index.test.mjs
index 9e49f885..29eec1cd 100644
--- a/typescript/tests/dsh-index.test.mjs
+++ b/typescript/tests/dsh-index.test.mjs
@@ -137,23 +137,18 @@ test("registers a ReMe settings namespace and reads changed values for new sessi
},
inject(names, callback) {
if (!names.includes("settings")) return;
- callback({
+ const settingsCtx = {
settings: {
- register(ns, _schema, options) {
- section = options.base;
+ installSection(owner, ns, _schema, base, hooks) {
+ assert.equal(owner, ctx);
+ section = base;
assert.equal(String(ns), "reme-memory");
- return {
- get: () => section,
- watch(listener) {
- notify = listener;
- },
- };
+ hooks.setSource(() => section);
+ notify = hooks.onChange;
},
},
- effect(execute) {
- return execute();
- },
- });
+ };
+ callback(settingsCtx);
},
};
apply(ctx, {