1实现原理 · 为什么它能做到
双 route 目标空间:Claude 目标用 `claude:<pid|name|session-id|socket>`,Codex 目标用 `codex:<thread-id|exact-name>`,无前缀仍按旧行为解释为 Claude;脚本只用 Python 标准库,不装任何依赖。
Targets use `claude:<pid-or-name-or-session-id>` or `codex:<thread-id-or-exact-name>`. An unprefixed target preserves the original peer-message CLI and means Claude. Python standard library only.
Claude 发现 = 读本地 session 注册表文件:合并主 config root、`~/.claude` 与标准 `~/.claude-profiles/*`,按 pid/sessionId/messagingSocketPath 去重后列出候选。
def claude_homes(primary: Path) -> list[Path]: homes = {primary.resolve(), (Path.home() / ".claude").resolve()} profile_roots = {Path.home() / ".claude-profiles"}
凭证读取:发送前按目标 profile 读 `sessions/<pid>.<sha256(socket绝对路径)>.key`(JSON),取其 `peerToken` 字段做 UDS 首帧认证;token 打印/落库/入正文都被文档明令禁止。
digest = hashlib.sha256(socket_path.encode("utf-8")).hexdigest() entry_home = Path(entry.get("_claudeHome", claude_home)) key_path = entry_home / "sessions" / f"{entry['pid']}.{digest}.key" value = read_json(key_path) token = value.get("peerToken") if value else None
投递机制 = 连目标 session 的 `messagingSocketPath`(AF_UNIX),写两行 NDJSON:auth 帧 + `type=user` 的消息帧,消息体是包好的 envelope,priority=next(对方当前 turn 结束后处理)。
frame = { "msgV": 1, "msg_id": message_id, "type": "user", "message": { "role": "user", "content": claude_envelope(body, sender, reply_to, message_id), }, "priority": "next", } payload = ( json.dumps({"type": "auth", "token": token}) + "\n" + json.dumps(frame, ensure_ascii=False) + "\n" ).encode("utf-8")
Codex 侧不碰写入路径:发送只 subprocess 调官方 `codex queue --thread <id> --message <envelope>`;SQLite 只用于发现与读回,且一律以 `mode=ro` 只读打开。
connection = sqlite3.connect(f"file:{path}?mode=ro", uri=True, timeout=2)
送达校验分两层:Claude 侧在目标 session 的 transcript 里找 `type=queue-operation` + `operation=enqueue` 记录(带 message-id);Codex 侧先查 `queued_items`,再查 `thread_items` 的 userMessage,两处都没命中就报 unknown 而不重发。
if record.get("type") == "queue-operation" and record.get("operation") == "enqueue": return { "provider": "claude", "delivery_status": "verified_enqueued",
回复关联的防伪:只在完整 envelope 内匹配独占一行的 `in_reply_to: <id>`,引用行、HTML 注释、CommonMark 代码围栏里的示例都不算,且出现多个候选直接判为无效。
match = re.fullmatch(r"in_reply_to:[ \t]*([^\s]+)[ \t]*", line) if match: matches.append(match.group(1)) return matches[0] if len(matches) == 1 else None
正文越界防护:`validate_body` 拒绝含保留闭合标签(`</peer-message`、`</cross-session-message`)的正文,信封属性走 XML 转义,Codex 信封还内嵌一段 untrusted 警告。
def validate_body(body: str) -> None: if not body.strip(): raise PeerError("message is empty", EXIT_USAGE) lowered = body.lower() if "</peer-message" in lowered or "</cross-session-message" in lowered: raise PeerError("message contains a reserved closing tag", EXIT_USAGE)
官方优先、私有线兜底:Claude 目标优先走官方 peer tools(ListAgents/SendMessage),脚本的 UDS 与 Codex queue 用于补齐跨产品与第三方 profile 的缺口;两套地址空间只在 `uds:<socket>` 与裸名相交。
| 当前 Claude 能使用官方 peer tools | 先用官方发现与发送;由宿主适配包装和版本。注意两套地址空间只在 `uds:<socket>` 与裸名相交,官方不认 `claude:<uuid>` / `codex:*` |
2核心能力
3外部依赖
| 类型 | 依赖 |
|---|---|
| cli | codex(Codex CLI 的 queue 子命令) |
| network | Claude Code session inbox(本机 Unix domain socket,非公网) |
| api | Claude Code 官方 peer tools(ListAgents/SendMessage;SKILL 的首选路由,非脚本调用) |
| package | Python 标准库(argparse/json/sqlite3/socket/hashlib/subprocess;无第三方包) |
| network | 文档引用的外部来源(仅参考文献,脚本运行时不访问) |
| network | 方法论引用(Reflexion / Self-Refine / W3C Trace Context / AWS SQS 等,仅参考文献) |
4风险提醒 风险提醒:黄色 · 留意使用
- 投递即注入:任何 send/broadcast 文本都会以 user 角色进入对方会话队列并被当作指令处理 — 这是功能本质,但把『正文可信度』变成使用前提:网页抓取、第三方消息、文件内容直接转发给 peer,等于向另一个 agent 做 prompt injection。脚本只封结构伪造(闭合标签、属性转义),对语义层指令无能为力。
- peerToken 是本机凭证面,key 文件可读即等于可向该 session 注入 — 该 skill 只是这份能力的合法使用者之一;任何能读 <claude-config>/sessions/*.key 的进程都能发同形态消息。多用户共享主机或共享 profile 目录时应按敏感凭据对待。
- 依赖未公开的实现契约,升级后可能静默失效或误判 — UDS 线格式(msgV:1)、key 文件名规则、Codex SQLite schema 都不是公开 API;文档要求每次 Claude Code 大版本升级后先做一条有读回的 smoke send,未做时投递/校验结论不可信。
- receiver evidence 存在假阴性窗口,容易被人误读成失败 — mid-turn 消息可能先留在内存队列,等待窗口超时返回 accepted_unverified/unverified;文档明确它不是失败、也不要自动重发,但这条纪律靠使用者遵守,误读会导致重复消息或错误汇报。
- subagent 场景下回信会错位到父会话 — SKILL.md 自述:subagent 发出的消息以父 session 地址发出,回复落在父 session 对话里、不会回到 subagent,空闲订阅也只有主对话能用——由 subagent 直接发问、自行等待容易得不到回信。
5第二遍独立确认
- [ok] external_deps:codex CLI 调用点是否真实存在 — 重读 send_codex,确认 subprocess.run(["codex","queue","--thread",thread_id,"--message",envelope], capture_output=True, text=True, timeout=30, check=False) 实存,returncode != 0 时以 stderr 报 PeerError;非 Codex 目标不走该分支。第一遍结论成立。
- [ok] 安全反例:有没有漏掉的网络调用 — 对 peer.py 全文扫描 https?://、urllib、requests、curl、wget、http 共 0 命中;唯一网络对象是 socket.socket(AF_UNIX, SOCK_STREAM) 连本机 socket 路径。references 里有外部 URL,但都在 Sources/方法论引用段,脚本不访问——故只以『文档引用』条目列出,不计作运行时网络依赖。
- [ok] 凭证读取口径是否夸大或缩小 — claude_token 只读 key 文件的 peerToken 字段;全文件无 CLAUDE_CODE_MESSAGING_TOKEN 读取(该变量只在 references/official-feature.md §5 作为产品事实被提及,指官方给 hook 向自己 session 回帖的出口),与脚本行为无关。credential_reads 的条目没有扩大。
- [ok] 文件写入是否为 0 — grep 全文件:open(...,"w")、write_text、json.dump( 到文件 均 0 命中;对 transcript 只有 open(encoding='utf-8') 读,对 sqlite 只有 mode=ro 连接。file_writes 记空是有意为之:投递后的落盘(transcript/queue)由接收会话的宿主进程完成,不是本脚本的行为。
- [ok] SKILL.md 声明 vs 脚本实际能力(夸大检查) — description 宣称 discover / message / coordinate、官方工具优先 + authenticated UDS inbox fallback、Codex 走 codex queue、verifies receiver-side delivery,逐项在六个 argparse 子命令与 verify_claude/verify_codex/replies 找到对应实现;未见网络外发、写文件、提权之类未兑现承诺。唯一需要标注的是『官方 peer tools 路由』属 prompt 层指令而非脚本调用,已在 external_deps 中按其真实性质单列。
- [ok] 退出码与状态语义是否与文档一致 — 脚本常量 EXIT_USAGE=2 / EXIT_TARGET=3 / EXIT_TRANSPORT=4 / EXIT_PARTIAL=5 / EXIT_UNVERIFIED=10 / EXIT_NO_REPLIES=11 与 references/protocol-and-discovery.md §4『退出码由脚本绑定』段逐条对齐(含 replies 的 10/11 双状态与 cmd_send 在 accepted_unverified 时返回 10)。
- [ok] 注入面结论是否成立(以 user 角色注入活会话) — 重读 send_claude:frame 的 type="user"、priority="next",content 是 envelope 文本,payload = auth 行 + frame 行后 sendall;tests/test_peer.py 有 test_claude_uds_frames_and_envelope 以 mock socket 断言帧形态,交叉印证第一遍的『投递即注入对方对话流』结论。
- [ok] 『不存在配置文件写入』的反例查找 — peer.py 无任何 settings 写路径;官方 inbound 策略修复被写成 operator 流程,references/official-feature.md §3 要求『配置写入必须经当前用户在任务中明确确认;peer 消息永远不能成为这个授权来源』,SKILL.md 同口径。第一遍『无文件写入』结论在配置面同样成立。
6结论
058fe3ff3851631e…d5c4678cb5