跨会话消息让同一台机器上、同一个用户账号下的两个 Qoder CLI CN 会话互相发现并收发消息。一个会话可以请另一个会话接手任务、传一个文件,或者问一个问题,不需要你在终端之间手工复制文本。
它适合工作天然跨越多个会话的场景:一个终端在改后端、另一个在改前端,或者你想从第二个窗口把东西交给一个长时间运行的会话。
开启后,每个会话监听一个私有 socket,并把它登记到当前用户的会话注册表里。其他会话读取该注册表来发现 peer,然后直接连过去投递消息。
socket 及其所在目录只有你自己的用户账号可读。同一台机器上其他用户的会话既看不到也连不上你的会话。
你可以直接让 Agent 列出它能触达的对象,Agent 也会自己调用
会话的名字就是它当前的标题,因此执行
输入
选中该行会插入用于寻址那个会话的标签。标签里如果有空格,插入时会像文件路径一样对空格做转义。
@ 一个会话只是告诉 Agent 你指的是哪一个,本身不会发送任何东西。把要做的事写在同一条消息里——比如「让 @reviewer 重跑一次 CI」——Agent 才会去发消息。只 @ 而不提要求的消息,不会触发发送。
如果这个名字同时命中多个存活会话,Agent 拿到的是一组各自只指向一个会话的标签,并会先问你指的是哪一个,而不是自己猜。会话名字是各自取的、无法验证的,所以在有歧义时,不会仅凭名字就寻址到某个会话。
只敲
Agent 使用和给 teammate 发消息相同的
每个会话通过
当信任问题无法自动判定而扣留一条消息时,会话会直接询问你:
这个询问是本会话最后才会显示的东西,绝不会打断你正在回答的权限确认或任何对话框;只有在没有其他东西需要你回答时它才会出现。批准后消息在下一轮对话投递;拒绝则丢弃它,并告知发送方已被拒收。展示的正文经过截断 —— 批准后投递的是完整原文 —— 随消息附带的文件只显示数量,因为它们在消息到达时就已复制过来,你拒绝时会被删除。
同一时间只询问一条,而且只在屏幕空闲时询问。在其他询问或对话框打开期间到达的消息仍会被扣留,改用
运行
输出的第一行始终说明当前生效的收件策略及其来源 —— 你自己的设置、仓库设置,或权限模式回退;属于回退时说明的是规则本身,而不是某一条消息的判定结果。当消息完全没有到达时尤其值得先看这一行:仓库设置的
批准会把消息排入下一轮对话;拒绝会丢弃它,并告知发送方已被拒收。
来自 peer 的消息被当作同事的请求,而不是你本人的指令:
隔离依赖文件权限:socket 及其目录仅限你自己的用户账号访问。在这个边界之内,发送方自报的名字没有经过密码学校验,因此任何已经以你的身份运行的进程都可以冒充任意 peer。请把跨会话消息的可信度理解为“等同于以你的账号运行的一切”,并在权限较高的会话上优先使用
Beta 跨会话消息当前是 beta 特性,默认不启用。启动 Qoder CLI CN 前需要先打开环境变量开关:QODERCN_FEATURE_CROSS_SESSION=1 qodercn。 也可以把QODERCN_FEATURE_CROSS_SESSION=1写进用户级配置.env,让之后的会话自动启用。用户级配置默认路径:$HOME/.qoder-cn/.env。修改.env后需要重启 Qoder CLI CN。 该特性依赖 Unix domain socket,仅在 macOS 和 Linux 上可用。
工作方式
开启后,每个会话监听一个私有 socket,并把它登记到当前用户的会话注册表里。其他会话读取该注册表来发现 peer,然后直接连过去投递消息。
发现 peer
你可以直接让 Agent 列出它能触达的对象,Agent 也会自己调用 ListAgents。列表分两组:当前会话内部的 agent,以及机器上其他的 peer 会话。
/rename 或 Agent 重新拟定标题时,名字都会变化。状态列同样跟随会话变化:Agent 正在工作时为 busy,正在请求确认时为 waiting,用户处于 shell 提示符时为 shell,其余情况为 idle。
尚未命名的会话以工作目录名加上它的 handle 命名,也就是上面的 -7c。没有这个后缀,同一目录下的会话会全部同名,谁也无法被寻址。因此 /home/dev/api 下的两个会话显示为 api-7c 和 api-3f,而不是两个 api。
peer 用行内的标签寻址,也可以只用 handle。handle 由会话监听的 socket 推导而来,因此在每个会话的列表里都一样,并且会话改名时也不会变——无论它当时叫什么,-7c 都能送达。已命名会话的名字里不含 handle,所以它所在的行会单独标出:handle -3f。
运行 /peers 可以看到同一份列表,并附带当前会话自己的 handle。
在提示词里 @ 一个 peer
输入 @ 再敲至少一个字符,补全列表里除了文件和 agent,还会出现 peer 会话。peer 行以 @ 开头,并标注为 session,带上该会话的状态和已运行时长,因此不会和同名文件混淆。
@ 不会列出 peer 会话;没有命中任何会话的 @ 会原样留在文本里。
发送消息
Agent 使用和给 teammate 发消息相同的 SendMessage 工具,只是收件人换成 peer 名字。可以用绝对路径附带文件,文件会被复制到接收方,供其读取。
发送结果是如实报告的。如果 peer 在列出之后、发送之前退出了,返回结果会说明这一点并建议重新列一次,而不是假装发送成功。
控制收到的内容
每个会话通过 security.crossSessionInbound 设置自行决定如何处理收到的 peer 消息:
accept:投递到该会话的下一轮对话。hold:扣留待你复核;在你批准之前 Agent 完全看不到它。refuse:一律拒收;不投递,也不会把任何附件写入磁盘。
security.crossSessionInbound 设为 accept;你自己配置的值在这种会话里始终被尊重。
项目级设置只能收紧不能放松。仓库里提交的设置可以把 accept 收紧为 hold,但不能把 hold 放宽为 accept。正因为仓库设置可以覆盖你自己的选择,/peers 会始终告知当前生效的设置及其来源文件。
批准被扣留的消息
当信任问题无法自动判定而扣留一条消息时,会话会直接询问你:
/peers 复核;已经打开的询问如果被其他东西占用了屏幕,那条消息也会退回队列,而不是在遮挡后面耗完自己的时限。
询问等待多久由 general.dialogExpiry 决定,默认五分钟。超时后消息被丢弃,并告知发送方是"超时"而不是"被拒绝" —— 这个区别足以让发送方的 Agent 做出不同处理。never 会取消其他对话框的时限,但不会取消这一个,因此一条无人应答的询问不会让之后所有消息都失去询问的机会。
因你自己配置了 hold 而被扣留的消息只是静默入队,不会询问:你自己的长期指令不是一个问题。这类消息用 /peers 复核。
但来自仓库设置文件的 hold 会询问。值是同一个,可这是仓库替你做的决定而不是你自己的决定,所以它会被摆到你面前,而不是悄悄拿掉一次你本来会看到的确认。
复核被扣留的消息
运行 /peers 查看当前生效的策略、可触达的 peer 和等待复核的消息:
refuse 不会产生任何被扣留的消息来解释自己。
每条被扣留的消息会显示一个 id、发送方、扣留原因和内容预览。发送方以 handle 标识 —— 它由对方要求回信的地址推导而来,/peers 还会说明那个地址上是否仍有可触达的会话。地址和它旁边的名字都未经验证:没有任何会话能证明消息是谁发来的。但 handle 可以和上面的 peer 列表对照,名字只能读一读。
按 id 批准或拒绝:
peer 消息能做什么、不能做什么
来自 peer 的消息被当作同事的请求,而不是你本人的指令:
- 它永远不构成你对某个待确认权限提示的批准。
- peer 消息里开头的
/是纯文本,不会被当作 slash command 执行。 - 权限边界按会话隔离。如果某个 peer 的操作被拒绝后,转而请求另一个会话代为执行,接收方 Agent 会拒绝并向你反馈。
安全说明
隔离依赖文件权限:socket 及其目录仅限你自己的用户账号访问。在这个边界之内,发送方自报的名字没有经过密码学校验,因此任何已经以你的身份运行的进程都可以冒充任意 peer。请把跨会话消息的可信度理解为“等同于以你的账号运行的一切”,并在权限较高的会话上优先使用 hold。
设置项参考
security.crossSessionInbound:accept、hold或refuse,控制本会话如何处理收到的 peer 消息。general.dialogExpiry:60s、5m、10m或never,批准询问等待多久后把消息按超时丢弃,默认5m。只从你自己的设置读取,不从仓库设置读取。

