news 2026/9/17 4:16:23

CodeCompanion.nvim 的 Agent Client Protocol (ACP) 支持:会话、工具与权限的完整技术解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CodeCompanion.nvim 的 Agent Client Protocol (ACP) 支持:会话、工具与权限的完整技术解析

CodeCompanion.nvim 的 Agent Client Protocol (ACP) 支持:会话、工具与权限的完整技术解析

【免费下载链接】codecompanion.nvim✨ AI Coding, Vim Style项目地址: https://gitcode.com/GitHub_Trending/co/codecompanion.nvim

ACP(Agent Client Protocol)是 CodeCompanion.nvim 连接外部编码 Agent(如 Claude Code、Codex、Gemini CLI)的开放协议通道,它让 Neovim 用户可以直接在聊天缓冲区中驱动独立的 AI 编码 Agent。本文以仓库中的 doc/agent-client-protocol.md 为主干,结合lua/codecompanion/acp/lua/codecompanion/interactions/chat/acp/下的源码实现,系统讲解 CodeCompanion 对 ACP 的能力覆盖、有状态会话管理、权限审批、会话恢复与模型切换机制,以及当前协议版本的已知限制,帮助你在实际配置与二次开发中快速定位所需能力。

ACP 是什么,CodeCompanion 如何接入

ACP 是一个开放的通信标准,用于规范“客户端(Client)”与“AI Agent”之间的结构化交互。CodeCompanion 作为客户端,通过 ACP 与独立的编码 Agent 进程通信,从而获得会话管理、文件系统操作、工具执行和权限处理等能力,这些能力全部在 Neovim 内部完成,无需离开编辑器。

从源码结构看,ACP 相关的实现被组织为两个层次:

  • 协议核心层:lua/codecompanion/acp/init.lua 中的Connection类负责进程生命周期、JSON-RPC 收发、会话建立与认证;lua/codecompanion/acp/methods.lua 集中定义了全部协议方法名。
  • 交互层:lua/codecompanion/interactions/chat/acp/ 下的handler.luacommands.luafs.luarequest_permission.lua等模块负责把协议事件渲染到聊天缓冲区、管理 Agent 动态注册的斜杠命令、执行文件读写与权限审批 UI。

功能实现总览:哪些能力已落地

文档给出了一张完整的能力对照表,CodeCompanion 对 ACP 规范的支持覆盖了核心协议、认证、内容类型、文件系统、MCP 集成、权限、会话管理与工具调用:

功能类别支持情况说明
核心协议JSON-RPC 2.0、流式响应、消息缓冲
认证多种认证方式、适配器级钩子
内容类型文本、图片、嵌入资源
文件系统支持行范围的文件读写
MCP 集成Stdio、HTTP 与 SSE 传输
权限带 diff 预览的交互式审批 UI
会话管理创建、列出、加载与恢复会话,含状态追踪
会话模式模式切换
会话模型选择指定模型
工具调用内容块、文件 diff、状态更新
Agent 计划Agent 执行计划的可视化展示
终端操作Agent 访问 Neovim 终端

其中“消息缓冲”在实现层面体现为Connection内的jsonrpc.LineBuffer:由于 JSON-RPC 的消息边界不一定与 I/O 边界对齐,lua/codecompanion/acp/init.lua 中的buffer_stdout_and_dispatch会先把 stdout 数据压入行缓冲,再逐行派发给handle_rpc_message解析,这是保证长流式响应稳定解析的关键。

支持的适配器与客户端能力声明

适配器生态

CodeCompanion 为 ACP 兼容的 CLI 工具预置了众多适配器,源码位于 lua/codecompanion/adapters/acp/,包括claude_codecodexgemini_clicopilot_acpcursor_cliopencodecagentcline_cligoosekilocodekimi_clikiromistral_vibeauggie_cli等。具体安装与配置方式见 doc/configuration/adapters-acp.md,那里给出了每种工具的前置安装步骤、认证方式与自定义命令示例。

每个 ACP 适配器都是一个结构化的 Lua 表,以 claude_code.lua 为例,它声明了:

  • commands.default:启动 Agent 的进程命令(如claude-agent-acp);
  • env:需要注入的环境变量(如CLAUDE_CODE_OAUTH_TOKEN);
  • parameters:初始化握手参数,包括protocolVersion = 1clientCapabilities
  • handlerssetupauthform_messageson_exit等生命周期钩子。

客户端能力声明

CodeCompanion 通过初始化握手向 ACP Agent 声明自身能力,文档给出了完整的声明内容:

{ fs = { readTextFile = true, -- Read files with optional line ranges writeTextFile = true -- Write/create files }, terminal = false -- Terminal operations not supported }

这段声明与源码中每个 ACP 适配器的parameters.clientCapabilities一致(见 claude_code.lua 第 33-35 行):文件系统读写能力被开放,而终端能力被显式关闭,Agent 无法获得 Neovim 终端访问权。

内容类型:能收发什么

文档用一张表明确了 ACP 通道的内容类型支持:

内容类型发送给 Agent从 Agent 接收
文本
文件 Diff不适用
图片
音频
嵌入资源

在实现层面,内容类型的解析由 lua/codecompanion/acp/prompt_builder.lua 的extract_text函数完成:它根据内容块的type字段区分textresource_linkresourceimageaudio,把非文本块转换为可展示的占位符(如[image][resource: uri]),再交给聊天缓冲区的渲染层。

图片发送则由 lua/codecompanion/adapters/acp/helpers.lua 的form_messages处理:只有当 Agent 通过promptCapabilities声明支持图片时才会生成image内容块,否则会记录一条警告日志。这也解释了为何 Agent 到客户端的图片(如截图回传)当前不被支持。

有状态会话:与 HTTP 适配器的本质差异

文档强调了一个关键设计差异:HTTP 适配器是无状态的(每次请求都携带完整对话历史),而ACP 适配器是有状态的——对话上下文由 Agent 维护,CodeCompanion 每次提示只发送新增消息,并通过session_id在整个会话生命周期内追踪状态。

这一点在 helpers.lua 的form_messages中得到印证:它只保留role == user_meta.sent未被标记的消息,即“Agent 还没看过的消息”,从而避免重复发送历史。相应地,连接对象Connection维护_initialized_authenticatedsession_id等状态标志,并通过is_connected()/is_ready()暴露给上层判断当前连接状态。

会话建立流程

从 lua/codecompanion/acp/init.lua 的调用链可以还原完整的会话建立流程:

  1. connect_and_authenticate():启动 Agent 子进程(start_agent_process),发送initialize握手请求,校验双方protocolVersion,随后进入认证阶段;
  2. _authenticate():优先调用适配器自定义的handlers.auth钩子(如 Claude Code 的 OAuth token 注入);否则读取 Agent 广播的authMethods,匹配配置的auth_method并发送authenticate请求;
  3. _establish_session():根据session_id是否存在以及 Agent 是否支持loadSession,决定发送session/load还是session/new。会话建立时还会携带cwdmcpServers参数。

认证顺序在源码中表现为“适配器钩子优先、协议认证兜底”:自定义钩子成功即完成认证,否则才尝试 Agent 广播的认证方法。若配置的auth_method未被 Agent 广播,连接会直接失败并给出可用的方法列表。

文件上下文处理:重新读取而非复用缓冲区

文档指出:当把文件作为嵌入资源发送给 Agent 时,CodeCompanion 会重新读取文件内容,而不是使用聊天缓冲区中的表示。这样做的目的是避免 HTTP 适配器专用的<attachment>标签出现在 ACP 消息中——该类标签对 LLM 适配器有意义,对 ACP Agent 则没有意义。

实际发送时(见 helpers.lua),文件与缓冲区上下文被转换为纯文本形式:

Sharing the following file as context: <path>

而 Agent 侧的主动文件读取则走fs/read_text_file/fs/write_text_file协议方法,实现在 lua/codecompanion/interactions/chat/acp/fs.lua:

  • 读取:支持linelimit参数实现行范围切片,若文件不存在则返回ENOENT,CodeCompanion 会把它当作空内容返回给 Agent,从而允许 Agent 直接创建新文件;
  • 写入:优先写入已打开的缓冲区(保持 Neovim 内的同步),否则直接写盘,成功后触发FileEdited事件供其他模块响应。

动态斜杠命令:Agent 自主宣传能力

ACP Agent 可以动态宣传自己的斜杠命令。CodeCompanion 在聊天缓冲区中通过\command访问这些命令,并在发送提示前自动将其转换为/command格式。

底层实现位于 lua/codecompanion/interactions/chat/acp/commands.lua:

  • Agent 通过session/update通知中的available_commands_update推送命令列表(由 acp/init.lua 的handle_available_commands_update接收);
  • 命令按session_id存储,并通过link_buffer_to_session把聊天缓冲区与会话绑定,实现按缓冲区查询可用命令;
  • 命令更新后会触发CodeCompanionACPCommandsUpdate用户事件,供补全插件(如 blink、cmp 等)刷新候选列表;
  • 缓冲区关闭时通过CodeCompanionChatClosed自动解绑,避免内存泄漏。

会话恢复:/resume的完整链路

如果 Agent 支持session/list能力,就可以在新的聊天缓冲区中用/resume斜杠命令恢复历史会话。文档描述的流程是:调用session/list发现历史会话,再调用session/load把所选会话的对话历史恢复到聊天缓冲区。

对应实现见 lua/codecompanion/interactions/chat/slash_commands/builtin/resume.lua,其中有两个重要细节:

  1. 能力预检:命令的enabled钩子会先检查can_list_sessions()can_load_session(),二者分别对应 Agent 广播的sessionCapabilities.listloadSession能力(见 acp/init.lua 第 282-295 行),不满足时命令直接禁用并给出原因提示;
  2. 分页拉取session_list支持max_sessions上限(默认 500)与nextCursor游标分页,并且session/load加载过程中收到的session/update通知会被收集,用于在加载完成后重放渲染出完整历史(restore_session),同时恢复会话标题并触发ACPChatRestored事件。

session/listsession/load的方法名定义可参见 lua/codecompanion/acp/methods.lua。

模型选择:session/set_model与配置项联动

CodeCompanion 实现了session/set_model方法,允许为当前会话切换模型。文档特别注明:该方法不属于官方 ACP 规范(官方规范中使用的是session/set_config_option),因此在未来版本中可能变化。

从 acp/init.lua 的实现看,模型选择依赖 Agent 在会话开始时通过configOptions广播的配置项(_apply_config_options):

  • get_models():从category == "model"type == "select"的配置项中提取当前模型 ID 与可选模型列表;
  • set_model(model_id):本质上是把set_config_option(opt.id, model_id)包装了一层;
  • flatten_config_options:处理 ACP 规范允许的分组选项(sessionconfigselectgroup),把分组内的值展开为扁平列表。

模型还可以通过配置直接预置,见 doc/configuration/adapters-acp.md 的 “Setting Default Session Config Options” 一节,支持三种方式:

-- 方式一:在 interactions 中直接指定 require("codecompanion").setup({ interactions = { chat = { adapter = { name = "codex", model = "gpt-5.4", }, }, }, }) -- 方式二:在适配器 defaults 中以静态字符串指定 -- 方式三:在适配器 defaults 中以函数动态计算指定 require("codecompanion").setup({ adapters = { acp = { codex = function() return require("codecompanion.adapters").extend("codex", { defaults = { session_config_options = { model = function(self) return "gpt-5.4" end, mode = "Full Access", -- 其他会话配置项 thought_level = "Xhigh", }, }, }) end, }, }, })

若想查看某个适配器具体支持哪些会话配置项,可以打开聊天缓冲区的 调试窗口(Debug Window)查看 Agent 广播的configOptions原始数据。

权限审批:带 diff 预览的交互式 UI

权限处理是 ACP 交互中最具实战价值的环节。当 Agent 需要执行工具调用时,会发送session/request_permission请求,CodeCompanion 通过 lua/codecompanion/interactions/chat/acp/request_permission.lua 呈现审批界面。

其核心机制包括:

  • 选项映射:将 ACP 的权限选项种类(allow_onceallow_alwaysreject_oncereject_always)映射到 CodeCompanion 统一的审批快捷键(接受、始终接受、拒绝、取消),键位定义来自 lua/codecompanion/interactions/chat/tools/labels.lua;
  • diff 预览:如果工具调用的内容块类型为diff(且 diff 非空),审批界面会优先展示旧文本/新文本的对比,支持q关闭、Next/Prev在 hunk 间跳转、以及按对应的审批键直接响应;
  • 响应协议:所有审批最终通过send_result(id, { outcome = ... })回给 Agent,selected携带所选optionId,取消则返回cancelled,这与 prompt_builder.lua 中handle_permission_request的响应构造保持一致。

生命周期与清理:VimLeavePre兜底

CodeCompanion 通过监听 Neovim 的VimLeavePre自动命令保证与 ACP Agent 的干净断开(见 acp/init.lua 第 149-156 行):即使 Neovim 异常退出,也会调用disconnect()向 Agent 进程发送SIGTERM,确保子进程被正确终止。

进程意外退出时,handle_process_exit会做完整的收尾:触发适配器的on_exit钩子、释放所有挂起的异步回调(防止协程悬挂)、重置认证/初始化/会话状态,并把活动提示标记为canceled

协议版本协商

CodeCompanion 目前实现的是ACP Protocol Version 1protocolVersion = 1)。协议版本在初始化阶段协商:如果 Agent 选择了不同的版本,CodeCompanion 会记录一条警告日志,但仍会继续运行,并遵循 Agent 选择的版本工作。对应源码在connect_and_authenticate中的版本比对逻辑(见 acp/init.lua 第 136-144 行)。

当前限制

文档明确列出了三个尚未实现的能力,配置前值得留意:

  • 终端操作terminal/*系列方法(terminal/createterminal/outputterminal/release等)均未实现,CodeCompanion 不会向 Agent 声明终端能力(客户端能力中terminal = false);
  • Agent 计划渲染:来自 Agent 的 Plan 更新会被接收并记录日志(handle_session_update中的plan分支),但目前不会在聊天缓冲区 UI 中渲染可视化执行计划;
  • 音频内容:音频既不能发送也不能接收。

相关资源

  • Agent Client Protocol 官方规范:ACP 的完整协议文档
  • 配置 ACP 适配器:各 CLI Agent 的安装与配置步骤、自定义适配器写法
  • 在聊天中使用 Agent 与工具:聊天缓冲区中与 Agent 交互的日常用法
  • MCP 配置:如何定义 MCP 服务器并通过mcpServers = "inherit_from_config"让 ACP Agent 自动继承
  • 核心实现源码:lua/codecompanion/acp/init.lua、lua/codecompanion/acp/prompt_builder.lua、lua/codecompanion/interactions/chat/acp/
  • 测试用例:tests/acp/tests/adapters/acp/目录下的test_acp.luatest_prompt_builder.lua等覆盖了连接、消息解析与提示构建等核心路径,可作为二次开发的行为参考

【免费下载链接】codecompanion.nvim✨ AI Coding, Vim Style项目地址: https://gitcode.com/GitHub_Trending/co/codecompanion.nvim

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 4:15:41

给老工控机装AI牙齿:PCIe转USB 2.0桥接实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 4:14:18

PLC程序解耦三阶实战:从数据隔离到实例化复用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 4:14:07

校园跑腿互助系统实战:基于Spring Boot3+Vue3+微信小程序全栈开发

1. 校园跑腿的困局&#xff1a;为什么我最终做了这套爱心互助系统上半年在学校信息中心帮忙&#xff0c;接触了不少同学关于校园跑腿的真实诉求。代拿快递、代买食堂饭、图书馆占座、临时帮忙打印资料&#xff0c;每天这类需求在微信群和 QQ 群里少说有几百条。但群里的接单模式…

作者头像 李华
网站建设 2026/9/17 4:13:10

元胞自动机实现人群疏散模型:MATLAB仿真全流程解析

前阵子有个学弟找我问毕业设计&#xff0c;题目是“基于元胞自动机的人口疏散模型MATLAB实现”。他最开始的理解特别乐观&#xff1a;把房间画成网格&#xff0c;人涂成几个格子&#xff0c;设定出口&#xff0c;然后一运行就能看到人流往门口涌&#xff0c;最后做两张曲线图收…

作者头像 李华