Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

ACP:Agent Client Protocol

ACP 是一个基于 stdio 的 JSON-RPC 2.0 协议,让编辑器和 IDE 能够将正在运行的 ZeroClaw 代理作为会话主机来驱动。它采用换行符分隔的 JSON,轻量、可流式传输,易于连接到子进程。

可以将其理解为“面向智能体的 LSP”:编辑器启动 zeroclaw acp,通过 stdin 发送提示词,并在 stdout 上接收会话更新。

你打算用它做什么

  • 一个提供“向代理询问此文件”命令的编辑器扩展
  • 集成终端复用器,打开带有代理会话的侧边窗格
  • 一个通过编程方式驱动代理的 CI 运行器,无需完整的网关设置
  • 任何希望在不使用 HTTP 且无需绑定端口的情况下使用代理会话的功能

协议形态:v1

所有消息均为 JSON-RPC 2.0 格式(以换行符分隔)。ZeroClaw 实现的是协议版本 1

initialize

握手。返回服务器能力。

→ {`jsonrpc`:"2.0","id":1,方法:初始化}
← {`jsonrpc`:"2.0","id":1,"结果":{
    protocolVersion: 1,
    agentCapabilities: {
      loadSession: true,
      promptCapabilities: {image: false, 音频: false, embeddedContext: true},
      mcpCapabilities: {"http": false, sse: false},
      sessionCapabilities: {resume: {}, 关闭: {}}
    },
    agentInfo: {
      "name": zeroclaw-acp,
      "title": ZeroClaw ACP,
      version: 0.7.x
    },
    authMethods: [],
    "_meta": {
      zeroclaw: {
        "默认模型": anthropic/claude-sonnet-4.6,
        "maxSessions": 10,
        "sessionTimeoutSecs": 3600
      }
    }
  }}

loadSession: truesessionCapabilities: {"resume": {}, "close": {}} 表示会话持久化已激活。如果在启动时无法打开 SQLite 存储,这三项都将缺失或为 false,并且 session/loadsession/resumesession/close 将返回 SESSION_NOT_FOUND 错误。

_meta.zeroclaw 携带基础 ACP 规范中未包含的 ZeroClaw 专属扩展字段。仅实现基础规范的客户端可以忽略此对象。

promptCapabilities.embeddedContext: true 表示客户端可以在 session/prompt 中发送嵌入式 resource 块,其中包含 base64 blob(见下文)。imageaudio 目前仍为 false。原生 ACP Image/Audio ContentBlocks 尚未通告。

服务器始终返回 protocolVersion: 1。如果你在客户端发送 protocolVersion: 0,仍会收到 1 的响应,v0 客户端在处理新的消息结构时会遇到解析错误;详见下方的版本兼容性

session/new

打开一个隔离的代理会话。

agentAlias 用于指定要使用的 [agents.<alias>] 配置项。当配置了多个 agent 时,此字段为必填项;当仅存在一个 agent 时,会自动选中该 agent,此字段可省略。该别名支持驼峰式 agentAlias、蛇形式 agent_alias 或简写形式 agent

通过 网关 WebSocket 端点进行连接时,连接 URL 还可以携带 ?agent=<alias> 查询参数。该值是连接级默认值,而不是配置更改。session/new 的别名解析遵循以下优先级:

  1. session/new 参数中显式指定 agentAlias / agent_alias / agent
  2. WebSocket URL 中的网关 ?agent=<alias>
  3. [acp].default_agent
  4. 恰好存在一个时唯一配置的 [agents.<alias>] 条目
  5. 无法解析任何别名时出错

每个已解析的别名,无论由哪个步骤选定,都必须指向一个已启用、可调度的智能体。未知别名以及已配置但被禁用的智能体会导致 session/new 失败并返回 -32602 INVALID_PARAMS。空白或仅包含空白字符的 ?agent= 将被视为不存在,并继续进入下一步。

独立的 zeroclaw acp(stdio 子进程)不读取 ?agent=;请改用显式的 agentAlias[acp].default_agent

可选的 cwd 参数(别名:workspaceDirworkspace_dir)固定每个会话的文件访问边界;它会成为 SecurityPolicy 中由所有文件工具强制执行的 workspace_dir。它不会重新定位代理自身的持久状态:每个代理的明文状态(MEMORY.mdIDENTITY.mdSOUL.md)位于解析后的代理工作区agent_workspace_dir(<alias>),即 [agents.<alias>] 工作区)中,该工作区仍是一个额外允许的根目录;共享的 SQLite 存储和 cron 状态位于 config.data_dir 下。这些都不是单一的守护进程级 workspace_dir。

→ {`jsonrpc`:"2.0","id":2,方法:"session/new","参数":{
    "agentAlias": myagent,
    当前工作目录: "/path/to/project"
  }}
← {`jsonrpc`:"2.0","id":2,"结果":{
    "会话ID": "s-ab12cd",
    workspaceDir: "/path/to/project"
  }}

cwd 在接收时会进行规范化,../ 遍历无法逃出预期根目录。显式指定的 cwd 将严格作为会话边界,包括代理工作区下更窄的子目录。

如果省略 cwd,服务器使用已解析代理的工作区目录([agents.<alias>] workspace),而不是守护进程的启动目录。唯一的特殊情况是规范化为安装根目录本身的 cwd:Thunderbolt 等客户端会发送 . 作为占位符,而该占位符会解析为守护进程的工作目录。这个唯一的占位符会被视为“没有实际意义的 cwd”,并同样回退到每个代理的工作区,因此上传内容和工具沙箱会留在守护进程根目录之外。任何其他显式路径(包括安装根目录下的路径)都会按给定路径固定,绝不会扩大范围。

session/prompt

发送提示。响应是一系列流式返回的 session/update 通知,以 session/prompt 结果作为结束。

prompt 参数接受纯字符串或内容片段数组:

  • “prompt”: “Summarise the changes in the last commit.”
  • **数组:**每个元素都是文本部分 {"text": "..."} 或 ACP 资源块:
    • 文本资源: {"type": "resource", "resource": {"uri": "file:///path/to/file.rs", "text": "<file contents>"}}. 编辑器 @ 符号附件与内联文本。
    • Blob 资源: {"type": "resource", "resource": {"uri": "file:///path/to/report.pdf", "mimeType": "application/pdf", "blob": "<base64>"}}。二进制嵌入内容(PDF、DOCX、图像等)。ZeroClaw 解码 blob,将其写入 {session.workspaceDir}/uploads/(按 SHA 命名),并在代理提示中显示标记(对于 image/*,显示 [Document: …][IMAGE: …])。解码后的最大大小为 10 MB;无效的 base64 或超大 blob 会返回 INVALID_PARAMS

各部分按其出现顺序以双换行符连接。Blob 摄取与存储无关(不会调用 RPC file/attach)。当 MCP 工具结果包含 resource+blob 内容时,也会使用同一个物化辅助程序(参见 MCP 嵌入式资源 blob)。

→ {`jsonrpc`:"2.0","id":3,方法:"会话/提示","参数":{
    "会话ID": "s-ab12cd",
    "提示": 总结最后一次提交中的更改。
  }}
← {`jsonrpc`:"2.0",方法:"session/update","参数":{
    "会话ID": "s-ab12cd",
    更新: {sessionUpdate: agent_message_chunk, "内容": {类型:"文本","文本":“最后一次提交...”}}
  }}
← {`jsonrpc`:"2.0",方法:"session/update","参数":{
    "会话ID": "s-ab12cd",
    更新: {sessionUpdate: "tool_call", toolCallId: tc-1, "title": "shell",
               类型: 执行, 状态: pending, rawInput: {...}}
  }}
← {`jsonrpc`:"2.0",方法:"session/update","参数":{
    "会话ID": "s-ab12cd",
    更新: {sessionUpdate: tool_call_update, toolCallId: tc-1,
               状态: 已完成, rawOutput: "..."}
  }}
← {`jsonrpc`:"2.0","id":3,"结果":{
    "会话ID": "s-ab12cd",
    "stopReason": "end_turn",
    "内容": 上次提交引入了……
  }}

正常完成时 stopReason"end_turn",当回合被 session/cancel 中断时为 "cancelled"。ACP 的完成信号是 stopReason;ZeroClaw 还为现有客户端包含当前最终的 content 字符串。

错误:

代码含义
-32000 SESSION_NOT_FOUND给定的 sessionId 没有活动会话
-32002 SESSION_BUSY当前会话已有一个提示词请求正在处理中,请等待其完成或先取消该请求
-32602 INVALID_PARAMS缺少或格式错误的 sessionId / prompt
-32603 INTERNAL_ERROR代理任务崩溃或回合失败

session/update 通知(agent → client)

ZeroClaw 在一次提示交互期间会发送四种 session/update 通知。判别字段是 update 内的 sessionUpdate 字段:

sessionUpdate发出时关键字段
agent_message_chunk每个流式文本令牌content.type = "text"content.text
agent_thought_chunk内部推理 token(启用时)content.type = "text"content.text
tool_call工具调用已发起toolCallIdtitlekindstatus: "pending"rawInput
tool_call_update工具调用完成toolCallIdstatus: "completed"rawOutputcontent[]

tool_calltool_call_update 上的 toolCallId 是稳定且相互关联的,完成某个调用的更新所携带的 toolCallId 与发起该调用时的 toolCallId 相同。

tool_call_update 上的 name 字段是 ZeroClaw 的扩展(基础 ACP 规范并不要求)。客户端可将其用于显示;忽略它也是安全的。

将文件传递给客户端 (deliver_file)

当智能体需要将工作区文件交回以供下载或预览时,会调用 deliver_file 工具(path、可选的 mimeType、可选的 title)。完成后,ZeroClaw 会发出一个普通的 tool_call_update,其 rawOutput / body 保持精简(仅有简短的人类可读摘要,包含 base64 转储,也包含机器尾部;每个交付字段都以结构化方式承载于类型化工具产物上)。标准的 tool_call_update.title 携带人类可读的聊天标签:调用方的 title(任意文本,例如 "Quarterly report"),或默认使用文件名。content 数组另外还包含一个标准的 ACP 嵌入式资源:

{
  类型: "内容",
  "内容": {
    类型: "资源",
    "资源": {
      "uri": "attachment://deliver/9f2c1a7b0e4d5f6a3b8c2d1e0f4a5b6c7d8e9f0a1b2c3d4e5f60718293a4b5c6d.pdf",
      "mimeType": "application/pdf",
      "blob": "<base64>"
    }
  }
}

uri 是一个不透明的、按内容寻址的身份标识:attachment://deliver/<sha256>.<ext>,即文件字节的完整十六进制 SHA-256 摘要。它是 URI 安全的,并具备完整 256 位摘要强度的抗碰撞能力,且绝不会从调用方提供的文件名派生。相同的完整摘要也是磁盘上的 uploads/ 存储名称,因此不同内容绝不会别名到同一个文件或同一个 uri。相同的 uri 会以结构化方式随工具结果(JSON 字段 uri)以及类型化的工具工件一起携带;面向模型的文本中没有机器尾附信息。在嵌入该 blob 之前,ACP 层会重新读取文件并重新计算此哈希,若不再匹配则拒绝附加;工具验证与交付之间发生的替换会被检测到,而不会被信任。Thunderbolt 等客户端会物化出站 blob,并构建一个以该 uri 为键的引用 ref-map;agent 必须将返回的 uri 复制到 <widget:document-result fileId="…"> / [N] 引用中,且不得编造前缀。聊天显示名称是标准的 tool_call_update.title(调用方的 title,否则为文件名)。ACP resource 对象上没有 filename 字段,且 title 仅用于显示,绝不是磁盘上的名称。

文件必须位于会话工作区内(与 file_read 位于同一沙箱);超大文件(>10 MB)会被该工具拒绝。

session/request_permission(agent → client,出站请求)

当某个工具需要用户审批时(通过 autonomy 配置中的 always_ask,或 ask_user/escalate_to_human 工具),ZeroClaw 会从 agent 向 client 发出一个 JSON-RPC 请求。client 必须先返回结果,工具调用才会继续执行。

← {`jsonrpc`:"2.0","id":zc-out-0,方法:"session/request_permission","参数":{
    "会话ID": "s-ab12cd",
    options: [
      {optionId: allow-once,  "name": 允许一次,  类型: allow_once},
      {optionId: allow-always,"name": 始终允许,类型: allow_always},
      {optionId: "reject-once", "name": 拒绝,      类型: reject_once}
    ],
    toolCall: {
      toolCallId: approval-...,
      "title": “批准 shell 操作?”,
      类型: 执行,
      状态: pending,
      rawInput: {tool: "shell", 摘要: git status --short},
      "内容": [{类型: "内容", "内容": {类型: "文本", "文本": git status --short}}]
    }
  }}
→ {`jsonrpc`:"2.0","id":zc-out-0,"结果":{
    结果: {结果: 已选择, optionId: allow-once}
  }}

服务器签发的 ID("zc-out-N")始终是以 zc-out- 为前缀的字符串。关联是有方向的:每个对等端仅根据自身的待处理请求映射来匹配响应,因此相同的文本 ID 可以在两个方向上独立处于传输中。

响应结构:

  • {"outcome": {"outcome": "selected", "optionId": "<id>"}},用户选择了某个选项
  • {"outcome": {"outcome": "cancelled"}},用户关闭了提示

如果客户端始终未响应(崩溃、网络中断、用户关闭 IDE),请求将在 sessionTimeoutSecs 后超时,工具调用将被拒绝。

ask_user 使用相同的 session/request_permission 机制,将问题的 choices 映射为权限选项。在 ACP elicitation RFD 落地之前,不支持自由格式(无 choices)的 ask_user。在 ACP 会话中调用不带 choicesask_user 会快速失败并返回明确的错误。

session/cancel (ZeroClaw 扩展)

中止正在进行的 session/prompt 回合。此方法是 ZeroClaw 的扩展,并非基础 ACP 规范的一部分。如果 ACP 后续标准化了一个有冲突的 session/cancel,ZeroClaw 会将其扩展迁移到 _meta/session/cancel

取消与停止的区别: session/cancel 会中止正在进行的提示轮次,并返回 stopReason: "cancelled",同时附带截至中断点为止累积的所有流式文本。session/stop 会在当前轮次完成后正常结束会话,它会等待该轮次完成,而不是中断它。

规范的参数名为 sessionIdsession_id 作为兼容性别名也可接受。

→ {`jsonrpc`:"2.0",方法:session/cancel,"参数":{"会话ID":"s-ab12cd"}}
← {`jsonrpc`:"2.0",方法:"session/update","参数":{
    "会话ID": "s-ab12cd",
    更新: {sessionUpdate: agent_message_chunk, "内容": {类型:"文本","文本":部分……}}
  }}
← {`jsonrpc`:"2.0","id":3,"结果":{
    "会话ID": "s-ab12cd",
    "stopReason": 已取消,
    "内容": partial...\n\n[已通过客户端取消此轮对话]
  }}

如果会话没有活动的轮次,则取消操作为空操作(noop),会静默成功而不报错。这遵循 ACP 通知语义:通知不得产生错误。

session/stop (ZeroClaw 扩展)

干净地结束一个会话。不属于基础 ACP 规范:这是 ZeroClaw 特有的。如果未来 ACP 规范修订版添加了语义不同的 session/stop,此项将被重命名为 _meta/session/stop

→ {`jsonrpc`:"2.0","id":4,方法:"session/stop","参数":{"会话ID":"s-ab12cd"}}
← {`jsonrpc`:"2.0","id":4,"结果":{"会话ID": "s-ab12cd", 已停止:true}}

session/update(客户端 → 服务器)(ZeroClaw 扩展)

ZeroClaw 还接受来自客户端的入站 session/update(以及旧版别名 session/event)通知,用于注入自定义事件。这不在基础 ACP 规范中:属于 ZeroClaw 特有功能。如果 ACP 规范后续定义了语义不同的入站 session/update,此功能将重命名为 _meta/session/update

会话持久化

ZeroClaw 会自动将 ACP 会话持久化到 SQLite。无需任何配置,每当 zeroclaw acp 启动或网关接受 WebSocket ACP 连接时,存储就会在 <workspace_dir>/sessions/acp-sessions.db 打开。如果该文件无法创建(只读文件系统、权限错误),服务器将回退到仅内存会话,并在 initialize 响应中将 loadSession 报告为 false

持久化的内容:

  • 会话元数据:sessionIdworkspaceDircreated_atlast_activity
  • 完整对话历史记录:每个已完成的 session/prompt 回合之后写入的所有 ConversationMessage,每个回合作为一个原子事务。

会话可在进程重启后保留。在某次 zeroclaw acp 调用中创建的会话,可以在之后的调用中加载或恢复,只要使用的是相同的 workspace_dir(因而使用的是相同的 acp-sessions.db 文件)。

会话不会被自动删除。使用 session/close 可以停用会话而不删除它,之后可通过 session/loadsession/resume 将其恢复。

session/load (ZeroClaw 扩展)

使用完整历史回放恢复先前持久化的会话。服务器会用存储的对话历史初始化 agent,然后在返回之前将该历史以一系列 session/update 通知的形式流式回传给客户端。客户端接收到的更新流与会话从未结束时所看到的完全一致。

→ {`jsonrpc`:"2.0","id":5,方法:session/load,"参数":{"会话ID":"s-ab12cd"}}
← {`jsonrpc`:"2.0",方法:"session/update","参数":{
    "会话ID": "s-ab12cd",
    更新: {sessionUpdate: agent_message_chunk, "内容": {类型:"文本","文本":“最后一次提交...”}}
  }}
← ... (remaining stored messages replayed as session/update notifications)
← {`jsonrpc`:"2.0","id":5,"结果":{}}

session/load 返回后,会话即处于活动状态,可以接受 session/prompt 调用。

恢复持久化会话时,服务器仅在存储的所有者别名对应的代理仍可调度的情况下才重用该别名。否则,将按运营商控制的 [acp].default_agent → 单代理链回退,并跳过所有已禁用的别名。网关 ?agent= 仅作为 session/new 的默认值,不会重新绑定恢复操作。

session_id 作为 sessionId 的 snake_case 别名被接受。

错误:

代码含义
-32000 SESSION_NOT_FOUND存储中不存在给定 sessionId 对应的记录
-32001 SESSION_LIMIT_REACHED当前活动会话数已达到 max_sessions 上限
-32602 INVALID_PARAMS会话已处于活动状态,请先调用 session/close
-32603 INTERNAL_ERRORSQLite 读取失败

session/resume (ZeroClaw 扩展)

不重放历史记录的情况下恢复先前持久化的会话。代理会被植入已存储的对话历史,因此在下一轮对话时拥有完整的上下文,但不会发出任何 session/update 通知。当客户端已从先前连接获取到历史记录、仅需恢复代理状态时,请使用此选项。

→ {`jsonrpc`:"2.0","id":5,方法:session/resume,"参数":{"会话ID":"s-ab12cd"}}
← {`jsonrpc`:"2.0","id":5,"结果":{}}

session/resume 返回后,会话处于活动状态,可以接受 session/prompt 调用。错误与 session/load 相同。恢复别名选择遵循与 session/load 相同的可调度所有者回退规则。

加载与恢复对比: 当意外断开连接后重新连接,且客户端需要根据存储的历史记录重建其 UI 时,使用 session/load。当客户端已经拥有历史记录(例如,已将其存储在本地),仅需恢复服务端的 agent 状态时,使用 session/resume

session/close (ZeroClaw 扩展)

停用活动会话:取消所有进行中的轮次,将该会话从内存中的活动集合移除,并注销 ACP 反向通道。SQLite 存储中的会话记录不会被删除,之后仍可通过 session/loadsession/resume 恢复该会话。

→ {`jsonrpc`:"2.0","id":6,方法:session/close,"参数":{"会话ID":"s-ab12cd"}}
← {`jsonrpc`:"2.0","id":6,"结果":{}}

session_id 作为 sessionId 的 snake_case 别名被接受。

如果会话当前未处于活动状态(它可能仍存在于存储中),则返回 SESSION_NOT_FOUND-32000)。

Close 与 stop 的区别: session/close 会停用会话,同时保留其持久化记录以供日后重新加载。session/stop 同样会将会话从内存中移除,但对存储的影响是相同的。两者都不会删除 SQLite 记录。

配置

default_agent string? · default

session/new 省略 agentAlias 且配置了多个 agent 时所使用的 agent 别名。当恰好只有一个 agent 存在时,无论此字段如何,都会自动选择该 agent。

将它放置在任何表面上:

网关仪表板

打开 /config/acp 并设置 acp.default_agent 字段。

zerocode

Config 窗格中,设置 acp.default_agent 字段。

zeroclaw config

zeroclaw config set acp.default_agent <value>

环境变量

导出此覆盖配置(POSIX shell;可放入 ~/.bashrc~/.zshrc.env 或 Dockerfile 中)。将 <alias> 替换为实际的别名:

export ZEROCLAW_acp__default_agent=
max_sessions integer · default 10

并发 ACP 会话的最大数量。默认值:10

将它放置在任何表面上:

网关仪表板

打开 /config/acp 并设置 acp.max_sessions 字段。

zerocode

Config 窗格中,设置 acp.max_sessions 字段。

zeroclaw config

zeroclaw config set acp.max_sessions <value>

环境变量

导出此覆盖配置(POSIX shell;可放入 ~/.bashrc~/.zshrc.env 或 Dockerfile 中)。将 <alias> 替换为实际的别名:

export ZEROCLAW_acp__max_sessions=
session_timeout_secs integer · default 3600

空闲会话超时时间(以秒为单位)。在此时长内没有任何活动的会话将被回收。默认值:3600(1 小时)。

将它放置在任何表面上:

网关仪表板

打开 /config/acp 并设置 acp.session_timeout_secs 字段。

zerocode

Config 窗格中,设置 acp.session_timeout_secs 字段。

zeroclaw config

zeroclaw config set acp.session_timeout_secs <value>

环境变量

导出此覆盖配置(POSIX shell;可放入 ~/.bashrc~/.zshrc.env 或 Dockerfile 中)。将 <alias> 替换为实际的别名:

export ZEROCLAW_acp__session_timeout_secs=

session/new 省略 agentAlias 且配置了多个 agent 时,会参考 default_agent;如果它不存在且恰好只有一个 [agents.<alias>] 条目,则会自动选择该 agent。

作为子进程运行 zeroclaw acp 时,该命令会无条件启动服务器。作为守护进程运行时,网关会通过 WebSocket 在 /acp 暴露 ACP,无需额外配置。网关客户端可以将 ?agent=<alias> 附加到该 URL,这样每个已配置的 agent 都可以由符合规范、每个端点对应一个 agent 的客户端访问;身份验证(AuthorizationSec-WebSocket-Protocol?token=)会在连接升级前强制执行,并且该查询参数不会授予超出在已配置 agent 中进行选择之外的任何访问权限。

运行中

作为子进程(典型的 IDE 集成):

sh

zeroclaw acp

该二进制文件读取标准输入,写入标准输出,在遇到文件末尾(EOF)时退出。

通过守护进程网关(远程或同主机):

正常启动守护进程。网关始终通过 WebSocket 在 /acp 提供 ACP,无需额外的配置标志。客户端可直接连接:对于多代理安装,使用类似 ws://127.0.0.1:8080/acp?agent=myagent 的 URL,这样 session/new 可以省略 agentAlias;也可以通过 zeroclaw-acp-bridge 连接,该工具将 stdio ACP 协议桥接到网关 WebSocket:

sh

zeroclaw-acp-bridge

网桥从与守护进程相同的配置中读取网关地址和认证令牌。当守护进程使用非默认配置目录运行时(例如 --config-dir /tmp/zeroclaw),请将网桥指向同一目录:

sh

zeroclaw-acp-bridge --config-dir /tmp/zeroclaw
# 或者等效地:
zeroclaw-acp-bridge --config-dir=/tmp/zeroclaw

如果您不希望依赖缓存的令牌文件,也可以直接通过 ZEROCLAW_ACP_BRIDGE_TOKEN 提供 bearer 令牌。

版本兼容性

ACP v0 客户端(使用扁平的 {streaming, maxSessions, ...} 初始化响应以及 kind: "text"|"tool_call" 的 session/update 结构)在连接到 v1 服务器时会遇到反序列化错误。判别字段和封装结构发生了破坏性变更。升级步骤:

  • 使用 sessionUpdate(而非 kind)来区分 session/update 通知。
  • session/prompt 结果解析为 {sessionId, stopReason, content}(而非 {finished, usage})。
  • 实现 session/request_permission 响应处理:审批机制已从服务器通知改为由客户端应答的 RPC。
  • session/new 中移除 systemPrompt 参数,该参数未被读取。

安全

ACP 继承运行配置的自治级别。当 [autonomy] level = "supervised" 时,中等风险的工具调用会通过 ACP 反向通道触发审批,即一个 session/request_permission 出站请求,客户端必须对其进行确认。在 full 模式下,工具调用无需审批即可执行,且 workspace_only 会被隐式禁用(agent 可以访问会话 cwd 之外的路径);forbidden_paths 仍然生效。

session/new 中的 cwd 会成为该会话中所有文件和 shell 工具使用的 SecurityPolicy 工作区边界。代理的系统提示词会反映同一个有效的会话工作区:提示词中的 “Working directory” 是根据 SecurityPolicy.workspace_dir 生成的(即会话的 cwd;如果省略 cwd,则为代理工作区),而代理的身份和个性(IDENTITY.mdSOUL.md)则从单独的代理工作区加载。因此,模型看到的目录就是其文件和 shell 工具实际以其为根目录的目录。

**双根文件权限。**设置会话 cwd 会将_文件和 shell 工具_路径(读取/写入/列出、shell 启动 CWD 以及嵌入资源 uploads/)限定到该目录。它不会使会话成为独占沙箱:解析后的 代理工作区仍是允许的根目录,因此无论会话 cwd 如何设置,代理自身的资源(技能、身份以及 [agents.<alias>] 下的每代理状态)都仍可访问。换句话说,workspaceDir 控制_会话文件操作_以何处为根,而代理工作区仍继续支撑代理自身配置作用域内的资源。当省略 cwd(或其为安装根占位符)时,二者会重合,因为会话本身以代理工作区为根。

内存

ACP 会话不会与智能体的持久化记忆系统交互。这是一个有意为之的设计决策:ACP 适用于 IDE 驱动的编码任务,而非长期的关系构建。

ACP 会话从代理配置中继承的内容:个性、技能、风险配置、运行时配置、模型提供商以及所有非内存工具。

ACP 会话不包含的内容:

  • 记忆工具(memory_recallmemory_storememory_forgetmemory_exportmemory_purge)不可用
  • 自动记忆调用(每轮对话基于长期记忆构建的上下文前导)已禁用
  • 会话自动保存到智能体记忆存储的功能已禁用

会话上下文来自 acp-sessions.db 中持久化的对话历史。会话是持久化的、可恢复的、可删除的,会话历史用作工作上下文,而非智能体的长期记忆。

这种分离确保临时的编码辅助对话不会污染智能体的长期记忆,同时来自聊天频道的无关知识也不会渗入 ACP 会话中。

代码参考

  • ACP 服务器:crates/zeroclaw-channels/src/orchestrator/acp_server.rs
  • ACP 反向通道:crates/zeroclaw-channels/src/acp_channel.rs
  • 会话存储(SQLite):crates/zeroclaw-infra/src/acp_session_store.rs
  • 网关 ACP-over-WebSocket 端点:crates/zeroclaw-gateway/src/acp.rs
  • 按会话路径强制执行:crates/zeroclaw-config/src/policy.rsSecurityPolicy::from_config)、crates/zeroclaw-runtime/src/agent/agent.rsfrom_config_with_session_cwd_and_mcp
  • 操作系统级沙箱检测/后端:crates/zeroclaw-runtime/src/security/detect.rslandlock.rsbubblewrap.rsseatbelt.rs

另见