OpenClaw 适配器深度指南:在 Pi Agent 会话中落地 context-mode 沙箱路由与压缩恢复
【免费下载链接】context-modeContext window optimization for AI coding agents. Sandboxes tool output (98% reduction), persists session memory, and enforces routing across 17 platforms via MCP + hooks.项目地址: https://gitcode.com/GitHub_Trending/cl/context-mode
context-mode 为 17 个主流 AI 编程 Agent 平台提供了统一的上下文窗口优化方案,而 OpenClaw 适配器(docs/adapters/openclaw.md)负责将这一能力接入OpenClaw 网关下的Pi Agent会话:通过拦截工具调用将高数据量操作路由进沙箱执行、把会话事件持久化到 SQLite 以便压缩(compaction)后恢复、并借助 MCP 边车暴露全套ctx_*工具。读完本文,你将掌握该适配器的安装排障流程、OpenClaw 插件两套 Hook API 的正确用法、会话连续性(Session Continuity)的底层实现,以及如何基于最低版本要求评估升级风险。
OpenClaw 适配器在 context-mode 中的定位
OpenClaw是一个管理 Agent 会话、扩展(extension)与工具路由的网关平台;Pi Agent则是 OpenClaw 内置的编程 Agent,运行在 OpenClaw 进程内,提供 Read、Write、Edit、Bash 等软件开发工具。
context-mode 的 OpenClaw 适配器只针对Pi Agent 会话挂接:在tool_call:before阶段拦截工具调用,把数据密集型操作路由进沙箱(沙箱输出可削减约 98% 的上下文占用,见 openclaw.plugin.json 的项目描述);在tool_call:after阶段跟踪会话事件,为后续压缩恢复准备快照。
支持的配置形态
- Pi Agent 会话(含 Read/Write/Edit/Bash 编码工具)——完全支持,是适配器的目标场景;
- 自定义 Agent(同样具备编码工具)——可能可用但未经过测试,因为适配器依赖工具名与 Pi Agent 约定的匹配关系。
平台能力矩阵
适配器在 src/adapters/openclaw/index.ts 中声明了自身能力,可直接对照理解其功能边界:
| 能力 | 取值 | 说明 |
|---|---|---|
preToolUse | true | 工具调用前拦截(路由裁决) |
postToolUse | true | 工具调用后捕获事件 |
preCompact | true | 通过registerContextEngine+ownsCompaction管理压缩 |
sessionStart | true | 通过command:newHook 触发会话初始化 |
canModifyArgs | true | 可在tool_call:before中原地修改参数 |
canModifyOutput | false | 不修改工具输出 |
canInjectSessionContext | true | 通过before_prompt_build生命周期钩子注入上下文 |
对应测试 tests/adapters/openclaw.test.ts 逐项断言了这些能力标志。
安装与前置条件
快速安装
npm run install:openclaw该命令实际执行scripts/install-openclaw-plugin.sh,一次性完成构建、扩展目录部署、运行时注册与网关重启。
前置条件
- Node.js 必须在 PATH 中(构建与注册步骤依赖);
- OpenClaw 必须至少启动过一次——安装脚本需要
openclaw.json,该文件在 OpenClaw 首次启动时生成; OPENCLAW_STATE_DIR必须指向 OpenClaw 状态目录(默认/openclaw),可通过参数覆盖:
npm run install:openclaw -- /path/to/state手动安装
适合高级用户或自定义部署:
bash scripts/install-openclaw-plugin.sh [OPENCLAW_STATE_DIR]脚本细节见 scripts/install-openclaw-plugin.sh。
安装脚本内部做了什么
从 scripts/install-openclaw-plugin.sh 的源码看,安装共分 6 步:
- 构建:
npm install+npm run build+npm rebuild better-sqlite3(为系统 Node 重建原生绑定); - 部署扩展:将
openclaw.plugin.json复制到$OPENCLAW_STATE_DIR/extensions/context-mode/,并生成一个指向构建产物的index.ts桩文件(OpenClaw 不跟随目录软链接,所以必须是真实目录); - 清理 jiti 缓存:删除
/tmp/jiti/下context-mode-index.*.cjs、build-adapters-openclaw-plugin.*.cjs等缓存文件,避免旧编译产物残留在升级后继续生效; - 验证发现:调用
openclaw plugins list检查context-mode是否被识别; - 写入运行时配置:委托 scripts/lib/register-openclaw-config.mjs 修改
openclaw.json; - 重启网关:向
node.*openclaw/dist/index.js进程发送SIGUSR1触发干净的重载,若网关未运行则提示手动openclaw gateway start。
第 5 步的配置写入(scripts/lib/register-openclaw-config.mjs)是幂等的,且做了三件关键事:
- 移除遗留的
plugins.load.paths条目(历史上会造成插件重复注册); - 确保
context-mode同时出现在plugins.allow与plugins.entries({ enabled: true }); - 注册
mcp.servers.context-mode→{ command: "node", args: ["<pluginRoot>/server.bundle.mjs"] },让 OpenClaw 以 MCP 边车方式拉起 MCP 服务器并暴露ctx_*工具。该逻辑对mcp.servers.context-mode采用“只覆盖command/args两个字段、保留其余字段”的保守策略,避免覆盖用户自定义的env、cwd、timeout等配置。
故障排查指南
“openclaw.json not found”
openclaw.json在 OpenClaw首次启动时才生成。这是“先装 context-mode、后启动 OpenClaw”的用户最常见的报错。解决方式:先启动一次 OpenClaw(openclaw gateway start),再重跑安装脚本。
“OPENCLAW_STATE_DIR (/path) does not exist. Is OpenClaw installed?”
状态目录在预期路径不存在。若你是通过 npm(而非 git clone)安装的 OpenClaw,需要确认其状态存储位置——常见路径为~/.openclaw或/openclaw,然后显式传参:
npm run install:openclaw -- /path/to/state插件已安装但未加载
清理 jiti 缓存后重启网关:
rm -f /tmp/jiti/context-mode-*.cjs若问题依旧,用openclaw plugins list确认插件是否出现在列表中。
插件加载了,但 Agent 工具列表中没有ctx_*工具
这是最容易混淆的一点:插件的 Hook 通过api.on(...)/api.registerCommand(...)注册,但Agent 可调用的ctx_*工具住在 MCP 服务器(server.bundle.mjs)里。OpenClaw 通过mcp.servers.context-mode声明将服务器作为 MCP 边车拉起,才把这些工具暴露给 Agent。安装脚本第 5 步会自动写入该条目;若你是手动配置,需检查openclaw mcp list并补上:
openclaw mcp set context-mode \ "{\"command\":\"node\",\"args\":[\"/absolute/path/to/context-mode/server.bundle.mjs\"]}" openclaw gateway restart重启后,Agent 工具清单中应能看到context-mode__ctx_execute、context-mode__ctx_search、context-mode__ctx_fetch_and_index等(OpenClaw 会给 MCP 来源的工具加上服务器名前缀)。
Hook 注册:两套 API 与同步 register 契约
适配器依据 OpenClaw 内部架构使用两套不同的注册 API,这是最容易踩坑的地方:
api.on()—— 用于生命周期与工具 Hook:session_start、before_tool_call、after_tool_call、before_compaction、after_compaction、before_prompt_build、before_model_resolve。它们是带结构化负载的类型化事件发射器;api.registerHook()—— 用于命令类 Hook:command:new、command:reset、command:stop,使用冒号分隔的事件名与通用 Hook 注册系统。
用错 API(例如用
api.registerHook("before_tool_call", ...))会静默注册成功但永远不触发。这个区分至关重要。
两类事件的常量定义集中在 src/adapters/openclaw/hooks.ts:HOOK_EVENTS(tool_call:before/tool_call:after/command:new/command:reset/command:stop)与LIFECYCLE_HOOKS(session_start/before_compaction/after_compaction/before_prompt_build/before_model_resolve等)。
同步 register() + initPromise 模式
OpenClaw静默丢弃register()的返回值——如果register()是 async 的,在其中注册的所有 Hook 都会丢失。因此适配器采用 initPromise 模式:同步返回,异步初始化先行启动,Hook 在首次触发时await该 Promise(见 src/adapters/openclaw/plugin.ts):
register(api): void { const initPromise = (async () => { /* async setup */ })(); api.on("after_tool_call", async (e) => { await initPromise; // handle event }); }完整的 Hook 注册清单
从 src/adapters/openclaw/plugin.ts 的实现看,register()内共注册了以下内容:
| Hook | 注册方式 | 职责 |
|---|---|---|
before_tool_call | api.on() | 路由裁决:deny 时返回{ block, blockReason },modify 时原地改写params |
after_tool_call | api.on() | 捕获会话事件并写入 SQLite |
command:new/command:reset/command:stop | api.registerHook() | 会话初始化 / 清理(cleanupOldSessions(7),保留 7 天) |
session_start | api.on() | 用 OpenClaw 的 sessionId 重键 DB 会话 |
before_compaction | api.on() | 将事件冲刷为恢复快照 |
after_compaction | api.on() | 递增 compact 计数 |
before_model_resolve | api.on() | 捕获用户消息(过滤系统包装消息) |
before_prompt_build(p=10) | api.on() | 向系统上下文注入恢复快照 |
before_prompt_build(p=5) | api.on() | 向系统上下文注入路由指令与技能引导 |
session_end | api.on() | 会话结束前固化最终恢复快照 |
subagent_spawning | api.on() | 为每个派生的子 Agent 注入路由块 |
| Context Engine | api.registerContextEngine() | 声明上下文引擎(ownsCompaction: false) |
/ctx-stats、/ctx-doctor、/ctx-upgrade | api.registerCommand() | 自动回复的斜杠命令 |
会话连续性:从工具调用到压缩快照
状态一览
| Hook | 注册方法 | 状态 |
|---|---|---|
after_tool_call | api.on() | 正常 |
before_compaction | api.on() | 正常 |
session_start | api.on() | 正常 |
command:new | api.registerHook() | 正常 |
command:reset | api.registerHook() | 正常 |
command:stop | api.registerHook() | 正常 |
事件捕获链路
after_tool_call处理时,适配器先把 OpenClaw 的小写工具名映射为 Claude Code 的 PascalCase 约定(src/adapters/openclaw/plugin.ts),再交给通用的事件抽取器:
| OpenClaw 工具名 | 映射后 |
|---|---|
exec | Bash |
read | Read |
write | Write |
edit/apply_patch | Edit |
glob | Glob |
grep/search | Grep |
同时兼容 OpenClaw v2+ 与旧版的字段差异:结果同时接受result(v2+)与output(旧版),错误同时接受字符串error(v2+)与布尔isError(旧版)。未识别的工具调用会以通用tool_call事件兜底入库,保证事件流不断裂。
session_start:基于 sessionKey 的重键
session_start事件携带sessionId与sessionKey(形如agent:<name>:main)。适配器用 src/adapters/openclaw/session-db.ts 的OpenClawSessionDB维护openclaw_session_map表:若该 key 已有更早的 sessionId,则调用renameSession()在一个事务内跨session_meta、session_events、session_resume、openclaw_session_map四张表整体改名,确保网关重启重键后已积累的事件、元数据与恢复快照全部存活。
压缩快照与注入
before_compaction:读取当前会话全部事件,用buildResumeSnapshot()构建恢复快照并 upsert;after_compaction:递增compact_count;before_prompt_build(priority 10):仅在compact_count > 0时把快照作为prependSystemContext注入一次(resumeInjected标志防止同会话重复注入);before_prompt_build(priority 5):注入动态生成的路由块(带<!-- context-mode: routing block injected (sessionID=...) -->可见标记)与技能引导文本。
优雅降级
如果压缩 Hook 因 OpenClaw 版本过旧而无法触发,适配器回退到DB 快照重建:直接基于after_tool_call已持久化到 SQLite 的事件重建会话状态。该快照不如 PreCompact 路径精确,但仍能保留关键状态(活动文件、任务、错误)。适配器不会在旧版本上崩溃,只是压缩恢复质量下降。
上游历史问题与最低版本要求
已解决的上游问题
- Issue #4967—— 压缩 Hook 不触发,被关闭为 #3728 的重复项,修复已合入;
- Issue #5513——
api.on()注册的 Hook 不响应工具生命周期事件,由 PR #9761 修复。
最低版本:OpenClaw > 2026.1.29
2026-01-29 发布的版本是首个包含 PR #9761 的api.on()修复的版本,因此适配器要求OpenClaw > 2026.1.29。
旧版本会坏掉什么:通过api.on()注册的生命周期 Hook(包括before_compaction、after_compaction、session_start以及工具拦截 Hook)可能静默不触发。
降级行为:压缩 Hook 不触发时回退到 DB 快照重建(见上一节),适配器不会崩溃,但压缩恢复质量降低。
Workspace 路由:多工作区会话隔离
OpenClaw 网关上可能同时跑多个 Agent 工作区,工具事件也可能跨会话交错投递。为此适配器内置了WorkspaceRouter(src/adapters/openclaw/workspace-router.ts),从 Pi Agent 会话元数据解析项目路径,确保会话数据库与路由指令按工作区隔离:
- 从工具调用参数中按
cwd > file_path > command的优先级提取/openclaw/workspace-<name>形态的路径; - 依据
sessionKey约定agent:<name>:main→/openclaw/workspace-<name>建立 workspace → sessionId 映射; session_start时注册映射,command:stop时移除映射;after_tool_call先用工作区路径解析正确的 sessionId,无匹配时才回退到闭包内 sessionId。
同时,OpenClawAdapter.getProjectDir()(src/adapters/openclaw/index.ts)按input.cwd > OPENCLAW_PROJECT_DIR 环境变量 > process.cwd()的优先级解析项目目录,保证下游 Hook 在 worktree 场景或平台省略cwd字段时仍能拿到确定的 projectDir。
ctx_* 工具面与 MCP 边车
插件通过api.registerTool()暴露 11 个ctx_*工具(src/adapters/openclaw/mcp-tools.ts,与 openclaw.plugin.json 中contracts.tools一致):
ctx_execute、ctx_execute_file、ctx_index、ctx_search、ctx_fetch_and_index、ctx_batch_execute、ctx_stats、ctx_doctor、ctx_upgrade、ctx_purge、ctx_insight。
这些工具与src/server.ts中 MCP 服务器注册的工具一一对应,处理器是刻意保持轻薄的委托层——转调捆绑的 CLI(cli.bundle.mjs),避免把整个 MCP 服务器栈搬进 OpenClaw 进程、缩小插件爆炸半径。路由块引导 Agent 调用这些工具,而工具真正执行时由mcp.servers.context-mode声明的 MCP 边车进程承载。
每轮令牌与成本捕获
OpenClaw 每轮(turn)会通过诊断事件总线发出一次model.usage事件,携带完整的用量拆分(input/output/cacheRead/cacheWrite)与预计算的costUsd。注意:原生的before_tool_call/after_tool_call中继只携带审批/策略数据,不含令牌用量,所以用量捕获不能从工具 Hook 获得。
适配器通过onDiagnosticEvent()(或在 SDK 缺失时经计算说明符动态导入openclaw/plugin-sdk/diagnostic-runtime)订阅该总线,交由 src/adapters/openclaw/usage.ts 的handleOpenclawUsageEvent()解析 → 构建 → 插入,全程不抛异常,用量捕获失败绝不会打断 Agent 轮次。costUsd优先于本地定价目录,确保成本统计与 OpenClaw 口径一致。
关键文件一览
| 文件 | 用途 |
|---|---|
| src/adapters/openclaw/plugin.ts | 主插件入口(同步 register、initPromise 模式、全部 Hook) |
| src/adapters/openclaw/index.ts | OpenClawAdapter:能力声明、输入解析、响应格式化、配置读写、doctor 校验 |
| src/adapters/openclaw/hooks.ts | Hook 事件常量与校验器 |
| src/adapters/openclaw/workspace-router.ts | 工作区路径解析与会话隔离 |
| src/adapters/openclaw/session-db.ts | OpenClawSessionDB:sessionKey 映射与会话重命名 |
| src/adapters/openclaw/usage.ts | model.usage每轮用量/成本捕获 |
| src/adapters/openclaw/mcp-tools.ts | 11 个ctx_*工具注册定义 |
| scripts/install-openclaw-plugin.sh | 一键安装器 |
| scripts/lib/register-openclaw-config.mjs | 幂等的openclaw.json运行时配置写入 |
| openclaw.plugin.json | 插件清单(id、contracts.tools、configSchema) |
| tests/adapters/openclaw.test.ts | 适配器单元测试(能力、解析、格式化、配置) |
小结与升级建议
OpenClaw 适配器的工程要点可以浓缩为三条:注册 API 选对(生命周期走api.on()、命令走api.registerHook(),用错即静默失效)、register 保持同步(异步初始化收敛进 initPromise,避免 Hook 整体丢失)、工具面与逻辑面分离(Hook 在插件进程内做路由与会话持久化,ctx_*工具由 MCP 边车承载)。部署时请先确认 OpenClaw 版本 > 2026.1.29、状态目录与mcp.servers.context-mode条目齐备;一旦遇到压缩恢复质量下降,优先核对压缩 Hook 是否被旧版本静默吞掉,再检查 jiti 缓存与openclaw plugins list的插件可见性。
【免费下载链接】context-modeContext window optimization for AI coding agents. Sandboxes tool output (98% reduction), persists session memory, and enforces routing across 17 platforms via MCP + hooks.项目地址: https://gitcode.com/GitHub_Trending/cl/context-mode
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考