AgnesCode ACP WebSocket 协议分析报告
协议端点: Renderer (React) ↔ agnesd (Rust Backend)
通信方式: WebSocket (JSON-RPC 2.0)
报告日期: 2026-07-25
分析版本: v1.0.17
1. 协议概述
ACP (Agent Client Protocol) 是 AgnesCode 的核心 AI 交互协议, 基于 JSON-RPC 2.0 标准, 运行于 WebSocket 之上。
┌─────────────────────────────────────────────────┐
│ Renderer (React) │
│ ┌───────────────────────────────────────────┐ │
│ │ $3 class (ACP Client) │ │
│ │ ├── initialize() → 会话初始化 │ │
│ │ ├── newSession() → 创建新会话 │ │
│ │ ├── prompt() → 发送提示词(核心) │ │
│ │ ├── cancel() → 取消请求 │ │
│ │ └── setSessionMode() → 设置模式 │ │
│ └──────────────────┬────────────────────────┘ │
│ │ WebSocket (JSON-RPC 2.0) │
│ ┌──────────────────▼────────────────────────┐ │
│ │ Z3 class (Transport Layer) │ │
│ │ ├── sendRequest() → 请求+等待响应 │ │
│ │ └── sendNotification() → 通知(无响应) │ │
│ └──────────────────┬────────────────────────┘ │
│ │ Stream │
│ ┌──────────────────▼────────────────────────┐ │
│ │ x8() WebSocket Stream │ │
│ │ ├── ReadableStream (接收 JSON 消息) │ │
│ │ └── WritableStream (发送 JSON 消息) │ │
│ └───────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘2. 连接建立
2.1 WebSocket URL
wss://127.0.0.1:{port}/acp?token={secretKey}构造函数 (从 main.js 提取):
javascript
function Vj(baseUrl, token) {
const r = new URL(baseUrl);
r.pathname = `${r.pathname.replace(/\/+$/, "")}/acp`;
r.protocol = r.protocol === "https:" ? "wss:" : "ws:";
r.searchParams.set("token", token);
return r.toString();
}2.2 连接生命周期
1. Renderer 调用 window.electron.getAcpUrl() 获取 WS URL
2. 创建 WebSocket 连接
3. 发送 JSON-RPC 2.0 initialize 请求
4. 收到 initialize 响应, 连接就绪
5. 开始会话交互 (session/new → session/prompt)
6. 断开时自动重连2.3 WebSocket Stream 实现
从 renderer 代码提取的底层实现:
javascript
function x8(url) {
const t = new window.WebSocket(url);
const o = []; // 接收队列
const a = []; // 等待队列
t.addEventListener("message", M => {
if (typeof M.data == "string")
o.push(JSON.parse(M.data)); // 解析 JSON
});
const readable = new ReadableStream({
async pull(M) {
await waitForData();
while (o.length > 0) M.enqueue(o.shift());
if (closed && o.length === 0) M.close();
}
});
const writable = new WritableStream({
async write(M) {
await connectionReady;
t.send(JSON.stringify(M)); // 序列化 JSON
},
close() { t.close(); },
abort() { t.close(); }
});
return { stream: { readable, writable }, isAlive };
}3. 消息格式 (JSON-RPC 2.0)
3.1 Request 消息
json
{
"jsonrpc": "2.0",
"id": 1,
"method": "session/prompt",
"params": { ... }
}3.2 Response 消息
json
{
"jsonrpc": "2.0",
"id": 1,
"result": { ... }
}3.3 Error 消息
json
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32601,
"message": "Method not found: xxx",
"data": { "method": "xxx" }
}
}3.4 Notification 消息 (无响应)
json
{
"jsonrpc": "2.0",
"method": "session/cancel",
"params": { ... }
}4. 标准 ACP 方法
4.1 方法清单
| 方法 | 类型 | 方向 | 描述 |
|---|---|---|---|
initialize | Request | C→S | 初始化连接, 协商能力 |
authenticate | Request | C→S | 认证 |
logout | Request | C→S | 登出 |
session/new | Request | C→S | 创建新会话 |
session/prompt | Request | C→S | 发送提示词 (核心方法) |
session/cancel | Notification | C→S | 取消当前请求 |
session/close | Request | C→S | 关闭会话 |
session/fork | Request | C→S | 分叉会话 |
session/list | Request | C→S | 列出会话 |
session/load | Request | C→S | 加载会话 |
session/resume | Request | C→S | 恢复会话 |
session/set_mode | Request | C→S | 设置模式 (smart/expert) |
session/set_model | Request | C→S | 设置模型 |
session/set_config_option | Request | C→S | 设置配置项 |
session/request_permission | Request | S→C | 请求权限 |
session/update | Notification | S→C | 会话更新推送 |
document/didChange | Notification | C→S | 文档变更 |
document/didClose | Notification | C→S | 文档关闭 |
document/didFocus | Notification | C→S | 文档聚焦 |
document/didOpen | Notification | C→S | 文档打开 |
document/didSave | Notification | C→S | 文档保存 |
nes/start | Request | C→S | 启动 NES (Nested Expert System) |
nes/suggest | Request | C→S | 建议 |
nes/accept | Notification | C→S | 接受 |
nes/reject | Notification | C→S | 拒绝 |
nes/close | Request | C→S | 关闭 NES |
terminal/create | Request | C→S | 创建终端 |
terminal/kill | Request | C→S | 终止终端 |
terminal/output | Request | C→S | 终端输出 |
terminal/release | Request | C→S | 释放终端 |
terminal/wait_for_exit | Request | C→S | 等待终端退出 |
fs/read_text_file | Request | C→S | 读取文本文件 |
fs/write_text_file | Request | C→S | 写入文本文件 |
elicitation/create | Request | C→S | 创建引导 |
elicitation/complete | Notification | C→S | 引导完成 |
4.2 协议能力协商 (initialize)
Request:
json
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": 1,
"clientInfo": {
"name": "AgnesCode",
"version": "1.0.17"
},
"clientCapabilities": {
"auth": { "terminal": false },
"fs": { "readTextFile": false, "writeTextFile": false },
"terminal": false,
"audio": false,
"embeddedContext": false,
"image": false,
"nes": null,
"positionEncodings": [],
"elicitation": { "form": {} },
"_meta": {
"agnes": {
"mcpHostCapabilities": {},
"customNotifications": true,
"recipeParameterRequests": true
}
}
}
}
}Response:
json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": 1,
"serverInfo": { "name": "agnesd", "version": "1.0.17" },
"capabilities": {
"experimental": {},
"logging": {},
"prompts": { "listChanged": true },
"resources": { "listChanged": true, "subscribe": true },
"tools": { "listChanged": true },
"completions": {},
"sampling": {},
"fs": { "readTextFile": true, "writeTextFile": true },
"terminal": true,
"auth": { "terminal": false },
"audio": false,
"image": false,
"embeddedContext": false,
"nes": null
}
}
}5. 核心方法: session/prompt
5.1 请求格式
json
{
"jsonrpc": "2.0",
"id": 3,
"method": "session/prompt",
"params": {
"sessionId": "uuid-v4",
"prompt": [
{
"role": "user",
"content": [
{ "type": "text", "text": "Hello, how are you?" }
],
"id": "msg-uuid",
"createdAt": 1234567890
}
],
"expectedRunId": "run-uuid"
}
}5.2 响应格式
json
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"sessionId": "uuid-v4",
"runId": "run-uuid",
"messageId": "msg-uuid",
"message": {
"role": "assistant",
"content": [
{ "type": "text", "text": "Hello! I'm doing well." }
]
},
"stopReason": "endTurn",
"model": "agnes-2.5-flash",
"usage": {
"inputTokens": 15,
"outputTokens": 10,
"totalTokens": 25,
"cacheReadTokens": 0,
"cacheWriteTokens": 0,
"cost": 0.0001,
"costSource": "provider_reported",
"elapsedMs": 1500,
"timeToFirstTokenMs": 500,
"isCompaction": false
},
"userMessageId": "user-msg-uuid"
}
}5.3 ContentBlock 类型
typescript
type ContentBlock =
| { type: "text", text: string, annotations?: Annotations, _meta?: any }
| { type: "image", data: string, mimeType: string, annotations?: Annotations, _meta?: any }
| { type: "audio", data: string, mimeType: string, annotations?: Annotations, _meta?: any }
| { type: "resource", resource: Resource, annotations?: Annotations, _meta?: any }
| { type: "resource_link", resource: Resource, annotations?: Annotations, _meta?: any }
| { type: "tool_use", name: string, id: string, input: any }
| { type: "tool_result", toolUseId: string, content: ContentBlock[], isError?: boolean }5.4 停止原因
typescript
type StopReason = "endTurn" | "stopSequence" | "maxTokens" | "toolUse";6. 其他会话方法
6.1 session/new
json
{
"jsonrpc": "2.0",
"id": 2,
"method": "session/new",
"params": {
"sessionId": "uuid-v4",
"cwd": "/Users/user/Documents/AgnesCode",
"additionalDirectories": [],
"title": "My Project",
"updatedAt": "2026-07-25T12:00:00Z",
"_meta": {}
}
}6.2 session/list
json
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"sessions": [{
"sessionId": "uuid-v4",
"cwd": "/Users/user/project",
"additionalDirectories": [],
"title": "Project Analysis",
"updatedAt": "2026-07-25T12:00:00Z",
"_meta": {}
}]
}
}6.3 session/set_mode
json
{
"jsonrpc": "2.0",
"id": 5,
"method": "session/set_mode",
"params": { "sessionId": "uuid-v4", "modeId": "smart" }
}6.4 session/set_model
json
{
"jsonrpc": "2.0",
"id": 6,
"method": "session/set_model",
"params": { "sessionId": "uuid-v4", "modelId": "agnes-2.5-flash" }
}7. 扩展方法 (110+)
所有扩展方法以 _agnes/unstable/ 为前缀, 分为以下子系统:
7.1 会话管理
session/info,session/export,session/importsession/history/load,session/extensions/*session/system-prompt/set,session/working-dir/updatesession/steer,session/archive,session/unarchive,session/renamesession/share/nostr,session/conversation/truncatesession/recipe/request-params,session/project/update
7.2 工具系统
tools/list,tools/call,tools/permissions/setresources/read
7.3 提供商配置
providers/list,providers/config/*,providers/secrets/*providers/catalog/*,providers/setup/catalog/*providers/custom/*,providers/supported-models/listproviders/canonical-model-info,providers/inventory/refresh
7.4 配方系统
recipes/list,recipes/delete,recipes/encode,recipes/decoderecipes/parse,recipes/save,recipes/scan,recipes/schedulerecipes/slash-command,recipes/to-yaml
7.5 配置
config/read,config/read-all,config/upsert,config/removeconfig/prompts/*,config/extensions/*
7.6 扩展
extensions/available
7.7 本地推理
local-inference/models/*,local-inference/huggingface/*local-inference/chat-templates/builtin/list
7.8 听写
dictation/config,dictation/transcribe,dictation/models/*dictation/secret/*
7.9 其他
side-chat/start,side-chat/cancel,side-chat/disposesources/*,apps/*,capabilities/listdiagnostics/get,defaults/*,preferences/*onboarding/import/*,scheduled-tasks/*,schedules/*agent-mentions/list,slash-commands/list
8. 错误码
| 代码 | 名称 | 描述 |
|---|---|---|
| -32700 | Parse Error | JSON 解析错误 |
| -32600 | Invalid Request | 无效请求 |
| -32601 | Method Not Found | 方法不存在 |
| -32602 | Invalid Params | 无效参数 |
| -32603 | Internal Error | 内部错误 |
| -32000 | Auth Required | 需要认证 |
| -32002 | Resource Not Found | 资源不存在 |
| -32800 | URL Elicitation Required | 需要 URL 引导 |
9. 传输层实现
9.1 消息序列化
javascript
// 发送: JSON.stringify
// 接收: JSON.parse
// 使用 WebSocket 原生文本帧9.2 请求-响应匹配
javascript
class Z3 {
#pending = new Map(); // id → { resolve, reject }
#nextId = 0;
async sendRequest(method, params) {
const id = this.#nextId++;
const promise = new Promise((resolve, reject) => {
this.#pending.set(id, { resolve, reject });
});
await this.#send({ jsonrpc: "2.0", id, method, params });
return promise;
}
#handleResponse(message) {
if (message.id !== undefined) {
const handler = this.#pending.get(message.id);
if (handler) {
if ("result" in message) handler.resolve(message.result);
else if ("error" in message) handler.reject(new Error(message.error.message));
}
}
}
}9.3 流式传输 (SSE)
ACP 支持通过 HTTP SSE (Server-Sent Events) 进行流式传输:
GET /acp
Accept: text/event-stream
Acp-Connection-Id: {connectionId}GET /acp
Accept: text/event-stream
Acp-Connection-Id: {connectionId}
Acp-Session-Id: {sessionId}