把acpx嵌入你的应用:acpx/runtime嵌入API与共享会话完全指南
【免费下载链接】acpxHeadless CLI client for stateful Agent Client Protocol (ACP) sessions项目地址: https://gitcode.com/gh_mirrors/ac/acpx
acpx不仅是命令行工具,它还是Agent Client Protocol(ACP)的无头客户端——通过acpx/runtime嵌入 API,你可以直接在 Node.js 应用里管理有状态的 AI 编码代理会话,无需启动子进程、无需解析终端输出。本指南带你掌握两种嵌入模式:进程内运行时与共享会话,让你把 Codex、Claude Code 等代理无缝接入自己的应用。
为什么选择嵌入:从"敲命令"到"调 API"
平时你用acpx codex "summarize this repository"时,其实每次都在启动进程、建立 ACP 连接。如果你的产品需要一个常驻的 AI 会话能力(IDE 插件、Web 后台、自动化编排器),每次都 spawn 进程就太慢了。
好消息是:acpx的 npm 包直接导出了运行时入口 🚀
"./runtime": "./dist/runtime.js"安装后即可import { createAcpRuntime } from "acpx/runtime",在进程内获得完整的会话管理、权限策略、事件流和模型控制能力。
两种嵌入运行时:一张表看懂怎么选
acpx/runtime提供两套运行时,对应两种典型场景:
| 对比项 | createAcpRuntime(进程内) | createSharedAcpRuntime(共享会话) |
|---|---|---|
| 会话所有者 | 你的应用自己持有 | 应用与 acpx CLI共享同一个本地会话 |
oneshot一次性会话 | ✅ 支持 | ❌ 仅persistent |
steer引导式回合 | ✅ 支持 | ❌ 仅prompt |
每回合权限回调onPermissionRequest | ✅ 支持 | ❌ 只能静态策略 |
| 自定义会话存储 / MCP 注入 / 子进程环境变量 | ✅ 支持 | ❌ 不支持 |
| 与终端 CLI 共用同一对话 | ❌ 各自独立 | ✅ 天然支持 |
💡选择口诀:会话只给你自己用 → 进程内;应用和终端要说同一句话、看同一段对话→ 共享会话。
三步跑通第一个进程内会话
进程内运行时的完整契约定义在 src/runtime/public/contract.ts,实现入口是 src/runtime.ts。核心流程只有三步:
- 创建运行时:指定工作目录、会话存储和权限模式
- 准备会话:
ensureSession按sessionKey + agent复用一个持久会话 - 发起回合:
startTurn提交提示词,消费events事件流,等待result
import { createAcpRuntime, createRuntimeStore } from "acpx/runtime"; const runtime = createAcpRuntime({ cwd: process.cwd(), sessionStore: createRuntimeStore({ stateDir: "~/.acpx" }), agentRegistry: undefined, // 使用内置代理注册表 permissionMode: "approve-reads", }); const handle = await runtime.ensureSession({ sessionKey: "reviewer", agent: "pi", mode: "persistent", }); const turn = runtime.startTurn({ handle, requestId: crypto.randomUUID(), mode: "prompt", text: "Summarize the repository", }); for await (const event of turn.events) { if (event.type === "text_delta") process.stdout.write(event.text); } console.log(await turn.result);会话状态默认落在~/.acpx/,重启应用后同名会话可以带着上下文继续聊——这就是"有状态"的含义。
共享会话:让终端和你的应用聊同一个天
这是嵌入 API 里最惊艳的能力 ⚡createSharedAcpRuntime()让你的应用和acpxCLI 连接到同一个本地会话所有者(源码见 src/runtime/shared.ts,官方文档见 docs/shared-sessions.md):
import { createSharedAcpRuntime } from "acpx/runtime"; const runtime = createSharedAcpRuntime({ cwd: process.cwd(), permissionMode: "deny-all", }); const handle = await runtime.ensureSession({ sessionKey: "reviewer", agent: "pi", mode: "persistent", }); const turn = runtime.startTurn({ handle, requestId: crypto.randomUUID(), mode: "prompt", text: "Summarize the repository", });创建好这个reviewer会话后,你的终端立刻就能加入同一场对话:
acpx pi -s reviewer 'Review the previous summary' acpx pi cancel -s reviewer acpx pi sessions show reviewer应用和终端不会互相抢占连接,也不会产生两个竞争性的代理会话。几个使用要点:
- 身份作用域:共享查找按
(agentCommand, cwd, sessionKey)精确匹配,不会向父目录回溯。终端必须与你的应用使用相同的用户、主目录、工作目录和代理命令。 - requestId 每次都要新的:重复使用正在排队的 ID 会被拒绝;它用于追踪请求,不提供幂等重试。
- 取消语义清晰:
turn.cancel()只针对当前请求,不会误伤终端正在跑的回合;runtime.cancel({ handle })则像 CLI 一样取消会话当前的活动回合。 - 只读旁听:
runtime.watchSession({ handle })可以旁观其他客户端的会话事件流,适合做实时 UI 面板。 - shutdown 只是断开:
runtime.shutdown()让客户端脱身并等待已接纳的操作收尾,不会杀掉共享所有者,也不会取消已接纳的回合。
回合生命周期:四个关键信号
无论进程内还是共享模式,startTurn返回的回合对象都由四个信号组成,理解它们就能优雅地驱动 UI:
| 信号 | 含义 | 用途 |
|---|---|---|
promptStarted | 传输层真正接受了提示词 | 排队结束后再亮"发送中"状态 |
events | 异步事件流(text_delta、tool_call、status等) | 打字机渲染、工具调用面板 |
result | 回合结束,completed/cancelled/failed | 收尾逻辑的唯一权威信号 |
cancel() | 取消本回合(也响应你传入的AbortSignal) | 用户点"停止" |
📌 老式写法
runTurn(...)会把done/error终止事件混进事件流,属于兼容适配器;新代码建议直接用startTurn,把实时事件和最终结果分开处理。
回合失败时result会带结构化错误:code、detailCode和retryable字段,你可以据此决定提示用户还是自动重试。
权限、诊断与错误处理
嵌入时最容易踩的坑是权限。运行时支持三档静态权限模式:
approve-all:自动批准第一个允许项approve-reads:自动批准读/搜索,其余询问deny-all:尽量拒绝——CI 和无人值守场景的推荐起点
进程内模式还可以传入onPermissionRequest回调,把审批弹到你自己的 UI 里;共享模式则只接受静态策略(因为回合实际运行在所有者进程中,无法回调你的进程)。
两个实用工具:
runtime.doctor():返回健康报告(ok/message/installCommand),启动时自检代理是否可用,比裸试错友好得多。AcpRuntimeError/isAcpRuntimeError:所有运行时错误都带稳定错误码(如ACP_BACKEND_UNAVAILABLE、ACP_TURN_FAILED),方便你写分支逻辑。
错误码全表可参考 docs/ACPX_ERROR_STRATEGY.md,CLI 侧退出码见 docs/exit-codes.md。
关键源码与文档索引
想深挖实现,按这个顺序读:
| 想理解什么 | 看哪里 |
|---|---|
| 嵌入 API 总入口与导出 | src/runtime.ts |
AcpRuntime契约、事件与结果类型 | src/runtime/public/contract.ts |
进程内运行时实现(AcpxRuntime类) | src/runtime.ts |
共享运行时(SharedAcpRuntime) | src/runtime/shared.ts |
| 会话所有者与队列生命周期 | docs/sessions.md、docs/2026-02-17-architecture.md |
| 共享会话完整语义(取消/断连/兼容) | docs/shared-sessions.md |
| 会话控制(cancel / mode / model / status) | docs/session-control.md |
| 内置代理注册表与自定义代理 | src/agent-registry.ts、docs/custom-agents.md |
| 权限模型 | docs/permissions.md |
常见疑问 FAQ
Q:共享会话和 CLI 的会话记录是同一份吗?是。sessionKey直接映射为 CLI 的会话名,查找使用精确的(agentCommand, cwd, name)作用域,应用和终端天然看到同一条记录。
Q:为什么共享模式不支持oneshot和steer?共享回合实际运行在会话所有者进程里,无法回调你的进程做交互式引导,也不能像进程内模式那样排队"旁观"活动回合——所以 API 在设计上直接拒绝这两种模式,避免隐式降级。一次性会话请用进程内运行时。
Q:findSession和ensureSession有什么区别?findSession只查本地记录,不会启动代理,适合"这个会话还开着吗?"的判断;ensureSession会按需创建或复用会话,可能拉起整个 ACP 连接。
Q:嵌入 API 稳定吗?acpx目前处于 1.0 之前,README 明确提示 CLI 与 runtime 接口仍在演进。建议锁定版本号,升级前跑一遍你的集成测试。
总结
acpx/runtime把"无头调用编码代理"从命令行脚本升级成了可编程的应用能力:
- 🎯 进程内
createAcpRuntime:完整控制——自定义存储、MCP 注入、交互权限回调、一次性会话一应俱全 - ⚡ 共享
createSharedAcpRuntime:应用与终端共用一个会话所有者,同一场对话、同一份记录、互不抢占 - 🧭 回合四信号(
promptStarted/events/result/cancel)让状态管理与取消逻辑清晰可预测
从npm install acpx开始,几十行代码就能给你的应用装上"持久 AI 会话引擎"。
【免费下载链接】acpxHeadless CLI client for stateful Agent Client Protocol (ACP) sessions项目地址: https://gitcode.com/gh_mirrors/ac/acpx
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考