OpenClaw ACPX 扩展实战:插件定位、依赖版本策略与本地验证流程全解
OpenClaw 的 ACPX 扩展是官方 ACP(Agent Client Protocol)运行时后端的宿主侧接入层,负责把外部编码 Agent(harness)以插件形式挂载到 Gateway。本文围绕 extensions/acpx/AGENTS.md 中沉淀的工程规范展开:先讲清这个扩展"薄封装"的设计定位,再完整继承其中的默认版本策略、未发布 ACPX 的开发流程、双 lockfile 机制、本地验证顺序与本地二进制策略,并结合 extensions/acpx/package.json、extensions/acpx/index.ts 与 extensions/acpx/src/config-schema.ts 等仓库源码,说明每一条规范背后的实现依据。读完后你可以独立完成 ACPX 插件的日常升级、临时接入未发布 ACPX 版本,以及在改动后按正确顺序完成验证。
定位:OpenClaw 对 acpx 包的薄封装
AGENTS.md 开宗明义:ACPX 扩展是已发布 acpx 包的"薄 OpenClaw 封装"(thin OpenClaw wrapper),可复用的 ACP 运行时逻辑应保留在上游 openclaw/acpx 仓库,而不是写进本扩展。
这一"薄封装"定位在仓库中可以得到印证:
- extensions/acpx/index.ts 是插件入口,它只做三件事:注册 Pi 会话目录(
registerPiSessionCatalog)、通过api.registerService注册 ACPX 运行时服务(createAcpxRuntimeService)、以及把reply_dispatch钩子接到插件 SDK 的tryDispatchAcpReplyHook上并附带超时控制。真正的 ACP 协议逻辑来自 npm 依赖,而非本目录实现。 - extensions/acpx/package.json 的
dependencies明确依赖已发布的acpx@0.13.1、@agentclientprotocol/claude-agent-acp@0.70.0、@agentclientprotocol/codex-acp@1.6.2、smol-toml与zod。也就是说,Claude Code 与 Codex 的 ACP 适配、ACP 会话与传输管理都由上游包承担,扩展侧只维护"接线"。
因此,在动手修改这个扩展之前,先判断改动性质:
- OpenClaw 侧的接线/配置/钩子问题 → 留在
extensions/acpx/; - 看起来是共享 ACP 运行时行为(而非 OpenClaw 专属胶水) → 按 AGENTS.md 的边界规则(Boundary Rule),应把改动挪到
openclaw/acpx上游仓库,在本扩展中通过消费依赖的方式使用,而不是在扩展内重新实现。
默认版本策略:永远指向已发布的 npm 版本
AGENTS.md 的默认版本策略(Default Version Policy)有三条硬性要求:
extensions/acpx/package.json默认应指向一个已发布的 npm release;- ACPX 正式发布后,不要把扩展停留在临时的 GitHub commit 或本地 checkout 上;
- 切回已发布包后,不要遗留临时的 pnpm build-script 白名单例外。
对照当前仓库的实际状态,这三条策略都成立:
- extensions/acpx/package.json 中
"acpx": "0.13.1"是一个确定的 npm 版本号,而非github:引用或 workspace 路径; - pnpm-workspace.yaml 的
allowBuilds段当前没有acpx: true条目——这正是策略第 3 条"清理临时白名单"在已发布版本状态下的应有结果。allowBuilds的用途是允许指定包运行安装期的 lifecycle/build 脚本(例如baileys: true、esbuild: true);当 ACPX 临时指向 GitHub commit 时,pnpm 可能拦截该临时包的构建脚本,需要临时加入acpx: true放行。
这条策略的实际价值在于:把"开发期临时 pin"与"发布态"严格区分开,避免主干上长期挂着不可复现的 commit 引用和一次性的构建放行例外。
未发布 ACPX 的临时接入流程
当 OpenClaw 需要用到尚未发布的 ACPX 变更时,AGENTS.md 给出了 8 步完整流程,这里逐步继承并结合仓库说明其用意:
-
先在上游
openclaw/acpx仓库完成代码改动。 保持"逻辑在上游、接线在本扩展"的边界不变。 -
在 OpenClaw 侧把
extensions/acpx/package.json临时指向所需的 ACPX GitHub commit。 即把acpx依赖从 npm 版本号临时改为对应 commit 的引用。 -
如果 pnpm 拦截该 GitHub 来源包的 ACPX lifecycle/build 脚本,临时在 pnpm-workspace.yaml 的
allowBuilds中加入acpx: true。 这是第 2 步的配套动作,因为 GitHub 来源的包通常不满足 pnpm 对构建脚本的信任要求。 -
刷新根 workspace 锁文件:
pnpm install --lockfile-only --filter ./extensions/acpx注意
--filter ./extensions/acpx:只让该扩展参与的依赖参与解析,范围可控;--lockfile-only表示只更新 pnpm-lock.yaml 而不落盘安装。 -
刷新扩展本地的 npm lock 以获得安装元数据:
cd extensions/acpx && npm install --package-lock-only --ignore-scripts这一步生成/刷新
extensions/acpx/package-lock.json,--ignore-scripts保证刷新元数据时不会意外执行被禁用的构建脚本。 -
重新构建 OpenClaw 并重启 Gateway,之后才做 ACP 实时验证。 ACP 运行时行为属于 Gateway 进程内的服务,不重启不会生效。
-
ACPX 发布后,把
extensions/acpx/package.json切回已发布的 npm 版本,并再次刷新同样的两份 lockfile。 与第 4、5 步对应,形成"pin 与 unpin"的对称操作。 -
移除只为 GitHub 来源 pin 而临时添加的
acpxbuild-script 白名单条目。 呼应版本策略第 3 条。
这 8 步可以归纳为一个闭环:上游改代码 → 本仓库临时 pin + 放行构建 → 双 lockfile 刷新 → 重建重启验证 → 发布后回切 + 清理例外。任何一步遗漏(尤其是第 5 步和第 8 步)都会让主干留下不一致的锁文件或残留的构建放行。
Lockfile 双轨制:pnpm 工作区锁与插件本地 npm 锁
AGENTS.md 单独用一节说明了两份 lockfile 的职责分工,这也是本扩展依赖管理中最容易踩坑的地方:
| 文件 | 角色 | 说明 |
|---|---|---|
pnpm-lock.yaml(仓库根) |
被 git 追踪的工作区锁文件 | 必须与 extensions/acpx/package.json 引用的 ACPX 版本保持一致,是 CI 与可复现安装的依据 |
extensions/acpx/package-lock.json |
插件包本地的安装元数据 | 对插件包(作为 npm 包被安装时)有用;如果当前仓库状态中它被 gitignore,重新生成它对本地验证仍然有用,只是不会出现在 git status 中 |
实践含义:当你完成第 4 步的根锁刷新后,还应执行第 5 步刷新扩展本地锁,两份锁都指向同一个 ACPX 版本时,状态才算一致;只改其中一份,本地行为与安装态可能出现偏差。
本地运行时验证顺序
AGENTS.md 对 ACPX 集成改动给出的推荐验证序列是:
pnpm install --filter ./extensions/acpx # 1. 只安装该扩展及其依赖
pnpm test:extension acpx # 2. 运行 acpx 扩展测试
pnpm build # 3. 重新构建 OpenClaw
# 4. 若 ACP 运行时行为或 bundled plugin 接线有变化,重启本地 Gateway
# 5. 若改动影响聊天中的直接 ACP 行为,重启后跑一次真实 ACP smoke
其中 test:extension 命令在根 package.json 中定义为 node --import ./scripts/tsx.mjs scripts/test-extension.mts,传入 acpx 即只测该扩展。扩展内已有覆盖配置解析、插件注册与懒加载等行为的测试,例如 extensions/acpx/index.test.ts、extensions/acpx/src/config.test.ts、extensions/acpx/src/command-line.ts 对应的分词逻辑等,可作为第 2 步的具体验证内容。
顺序之所以重要:先装依赖再测,保证测的是刚 pin 的版本;测试通过后再构建,避免把坏版本带进产物;只有当改动触及 ACP 运行时行为或 bundled plugin 接线时才需要重启 Gateway——纯胶水代码改动不需要。最后一步"真实 ACP smoke"针对的是端到端链路(聊天消息 → ACP 会话 → 外部 harness 执行 → 回复分发),这是单元与集成测试无法完全替代的。
直接 ACPX 二进制策略:用插件本地二进制做验证
AGENTS.md 的 Direct ACPX Binary Policy 规定:
- 优先使用插件本地的 ACPX 二进制,即
extensions/acpx/node_modules/.bin/acpx; - 不要依赖全局安装的
acpx二进制 来做 OpenClaw 的 ACP 验证; - 当插件本地二进制缺失或版本不对时,从
extensions/acpx/package.json中 pin 的版本重新安装它。
这条策略保证验证时运行的 acpx 版本与 extensions/acpx/package.json 声明的版本(当前为 acpx@0.13.1)严格一致,排除"全局装了新版本、插件用的旧版本"这类环境偏差导致的误判。结合上文的工作区配置,pnpm 的 nodeLinker: isolated 与 verifyDepsBeforeRun: false(见 pnpm-workspace.yaml)意味着脚本命令不会自动校正共享安装,因此"以插件本地 node_modules/.bin/acpx 为准"是刻意为之的确定性选择。
补充:扩展的运行时配置面
AGENTS.md 本身聚焦工程流程,但要真正读懂这个扩展的"OpenClaw 侧接线",有必要了解它暴露给 Gateway 配置的参数面。这些参数由 extensions/acpx/src/config-schema.ts 中的 Zod schema(AcpxPluginConfigSchema)统一校验并补默认值,是运行时配置的单一事实来源:
| 配置项 | 类型 / 取值 | 默认值 | 说明 |
|---|---|---|---|
cwd |
非空字符串 | — | ACPX 会话的工作目录 |
stateDir |
非空字符串 | — | 状态目录 |
probeAgent |
非空字符串 | — | 探测用 agent |
permissionMode |
approve-all / approve-reads / deny-all |
— | 交互工具请求的权限策略 |
nonInteractivePermissions |
deny / fail |
— | 无法向人请求批准时的策略 |
pluginToolsMcpBridge |
boolean | — | 是否桥接插件工具到 MCP |
openClawToolsMcpBridge |
boolean | — | 是否桥接 OpenClaw 工具到 MCP |
timeoutSeconds |
数值 ≥ 0.001 | 120(DEFAULT_ACPX_TIMEOUT_SECONDS) |
ACPX 运行时回合的默认会话超时 |
piSessionCatalog.enabled |
boolean | true |
是否启用 Pi 会话目录 |
mcpServers |
Record<string, { command, args?, env? }> |
— | 自定义 MCP server 命令配置 |
agents |
Record<string, { command, args? }> |
— | 自定义 agent 命令映射 |
其中 timeoutSeconds 在入口处的用法值得注意:extensions/acpx/index.ts 的 resolveReplyDispatchTimeoutMs 读取 api.pluginConfig.timeoutSeconds,仅当其为有限正数时生效,否则回落到 120 秒,再经 finiteSecondsToTimerSafeMilliseconds 换算为毫秒;reply_dispatch 钩子据此构建超时 AbortController,并与外部 ctx.abortSignal 通过 AbortSignal.any 合并(见 extensions/acpx/index.ts)。这解释了为什么验证流程强调"重启 Gateway 后再做实时验证"——这些超时与钩子是在插件 register 时求值并注册到 Gateway 进程中的。
此外,插件的构建配置(openclaw.build.staticAssets)会把 extensions/acpx/src/runtime-internals/mcp-proxy.mjs 与 extensions/acpx/src/runtime-internals/mcp-command-line.mjs 作为静态运行时资产输出,供 ACP 进程桥接使用;mcp-command-line.mjs 的分词逻辑与 extensions/acpx/src/command-line.ts 中 splitCommandParts 的简单引号/反斜杠规则相呼应,支持配置中的带引号命令串而无需调用 shell 解析器。
边界规则总结
把 AGENTS.md 的 Boundary Rule 与全文规范合起来看,ACPX 扩展的工程纪律可以浓缩为三句话:
- 逻辑归属:共享 ACP 运行时行为归
openclaw/acpx上游,扩展内只做 OpenClaw 专属胶水; - 版本一致性:
package.json、根pnpm-lock.yaml、扩展本地package-lock.json与插件本地.bin/acpx二进制四处版本必须对齐,且主干默认指向已发布 npm 版本; - 验证闭环:
pnpm install --filter→pnpm test:extension acpx→pnpm build→ 按需重启 Gateway → 按需真实 ACP smoke,任何"pin 临时版本"操作都必须以"回切 + 清理白名单"收尾。
遵循这套流程,你可以安全地在 OpenClaw 主干上消费 ACPX 的每次演进,同时避免临时 pin、构建放行例外与不一致锁文件在仓库中残留。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00