← 首页

手机是怎么看到终端里的 Agent 的

结论:手机并没有显示终端。电脑替你看着每个终端,算出「它现在是什么状态、在问什么、说了什么」,再把这些结果加密传给手机。手机显示的是卡片和聊天。

打个比方:电脑是在现场看守的人,手机是打电话问他的你。你问「现在怎么样」,他告诉你「3 号在等你回答,它问能不能删文件」。你说「可以」,他走过去替你按键。你没有看到现场的屏幕,但事情办了。

1. 总览:一共五段

你的电脑云上的中继手机Claude / Codex…跑在真实终端(PTY)里会话日志~/.claude、~/.codex…mooring serve后台,全程在跑算出每个会话的状态mooring remote--relay一部手机一条会话这里解密、查权限mooring-relay只转发看不到内容手机 App卡片 + 聊天没有终端画面按键写入屏幕文字读日志本机socket电脑和手机都是主动连过来端到端加密(Noise):只有这两头能解开WSSWSS
部分它做什么代码
AgentClaude、Codex 等,跑在 Mooring 开的真实终端(PTY)里。它们不知道手机存在。—
mooring serve后台。持有所有终端,读屏幕和日志,算状态。App 关了它也还在。crates/mooring-host/src/host/
mooring remote --relay电脑主动连上中继并保持连接。每部手机一条会话,在这里解密、检查权限,再转给后台。host/src/relay.rs、remote.rs
mooring-relay公网上的转发站。手机和电脑都连不到对方,就都连它。只存令牌的哈希,看不到内容。crates/mooring-relay/
手机 AppReact Native(Expo),iOS 和安卓是同一份代码。管连接、加解密、显示。mobile/src/

为什么要中继:家里的电脑和外面的手机通常没法直接连。两边都主动往外连同一个公网地址,就接上了。

2. 手机看到的状态是怎么来的

这是整个设计的核心。Agent 不会主动告诉手机任何事,状态全靠电脑从几个来源拼出来,并且只在一个地方判定,所以电脑和手机显示的词永远一样。

屏幕弹框、输入框的文字日志这一轮开始或结束HookCLI 主动报告程序上报OSC 7501进程后台命令还在不在每个会话的内容turn、waitingapproval、backgroundstatus::of唯一算状态的地方电脑的侧栏和鸟瞰显示状态词手机的卡片同一个状态词
来源告诉我们什么例子
日志这一轮开始、结束,说了什么。聊天内容都从这里来。Claude 的 jsonl;OpenCode 的 SQLite
HookCLI 在关键时刻主动报告Claude 一轮结束时报后台还有哪些 shell
屏幕日志里没有的「此刻」:弹框正等着你OpenCode 的权限框
程序上报程序通过终端转义序列报告自己的状态Codex 报 working
进程后台命令还活着吗,开了端口吗测试算「在跑」,http.server 算「等后台」

3. 配对:只发生一次

你电脑 Mooring中继手机电脑上发起配对,选权限为这部手机生成两把钥匙:路由码、加密密钥,存本机只告诉中继:路由码的哈希显示二维码二维码里才有原始钥匙:mooring://pair?v=2&relay=…&code=…&device=…&key=…&machine=…&name=…。它不经过中继。手机扫码钥匙存进系统钥匙串(Keychain / Keystore)

关键:原始钥匙只走二维码,从不经过中继。中继只有哈希,就算被人拿到,也解不开内容,也算不出钥匙。每部手机有自己的钥匙,所以可以单独取消某一部。

4. 连接:每次打开都走一遍

手机中继mooring remotemooring serve① WSS 连 /c/<路由令牌>/keepline算令牌的哈希,找到是哪台电脑② wopen:有手机来了③ Noise 握手第一条(中继原样转)④ Noise 握手第二条:钥匙对不上就 4401从这里起,每条消息都加密。中继只转字节,看不懂。蓝线是加密通道。⑤ hello(报上设备 id)⑥ 回复:角色、允许的操作⑦ 订阅 events(状态一变就通知)⑧ cards(要会话卡片)读工作区所有会话和状态卡片,手机进入「在线」之后:每 15 秒 ping 一次;请求 10 秒没回就算线路断了;回到前台马上刷新。

5. 完整一趟:手机发一句话

手机中继mooring remotemooring serveAgent你输入一句话;连不上就先存进「未发送」send:文字、回车、一次性 id(穿过中继)查权限:只有「完全控制」能发转成内部请求同一个 id 已做过就不再做(断线重发不会发两遍)把字写进终端开始干活,把回复写进自己的日志读到日志,更新内容和状态events:状态变了通知手机,手机刷新卡片聊天页开着时每 2 秒:chat after=最后一条新消息,显示成气泡

这里能回答你最初的问题:手机上的字是怎么进终端的?就是电脑替你把字写进了那个终端,Agent 收到的和你在键盘上敲的一样。回复则走另一条路:Agent 写日志,电脑读日志,手机每 2 秒来取一次。

6. 答审批:为什么要再读一遍屏幕

Agentmooring serve手机弹出权限框,停下来等屏幕安静后读一遍,用屏幕规则认出它状态变「等你回答」,卡片带上 approval(问题原文)你点「允许」answer:问题原文 + allow再读一遍屏幕:问题还是这句吗?光标在第一项吗?都对才按键:回车是允许,Esc 是拒绝问题对不上就报错,不按键:防止点的时候 Agent 已经换了问题。

OpenCode 走的是另一条路。它的权限框是横排按钮、没有编号,屏幕上也看不出光标在哪,靠读屏幕按键不安全:按错可能变成「永久允许」。所以 Mooring 改问 OpenCode 自己的本地接口:查到待答的问题,回答时再查一次确认还是这一条,然后直接调用回答接口,不模拟按键。手机上的按钮和确认流程和其他 CLI 一样。

情况手机上
从 Mooring 启动的 OpenCode有「允许」「拒绝」「以后都允许」
在终端里手动敲 opencode能看到「等你回答」,但没有按钮:它没开接口,要回电脑上答

本地端口的安全:开了 HTTP 端口后,你浏览器里打开的任意网页都可能请求它,我实测跨站请求确实能打到。所以 Mooring 每次启动 OpenCode 都给一个随机密码,放在它的环境变量里,再由 Mooring 从该进程读出来用。没有密码的请求会收到 401。

7. 状态机

7.1 手机和一台电脑的连接

打开 App 或扫码connecting连接中online在线offline连不上,会自己重试refused被拒,不再自动重试握手 + hello 成功连不上、超时4401、44044401、4404断线、1012、10134400、4410、4411重新扫码等 1、2、4…最多 30 秒;回到前台或收到 4411 时立刻重试
关闭码意思手机显示之后
1012电脑离开了中继offline退避重连
1013手机太慢被中继断开offline退避重连
4400手机发了协议不允许的内容offline退避重连
4401没配对、钥匙不对或已取消refused不再重试,要重新扫码
4404电脑换了配对码refused不再重试,要重新扫码
4410电脑上点了断开这部手机offline退避重连,5 分钟后才能再连上
4411权限改了offline立刻重连,拿到新权限

7.2 一个会话(电脑和手机共用)

新开的fresh在跑running等你回答waiting等你验收review等后台background出错了error停下了paused做完了closed稍后再看deferred很久没动stale你发第一句弹框或提问答了这一轮结束你又发一句:新一轮结束,但留着服务报错你中断、拒绝你标记完成你说稍后太久没动只有你能把会话标成「做完了」,Agent 不能。后台任务(测试、构建)还在跑时,回合结束了也算「在跑」。

手机的卡片还带一个更粗的 phase:在跑是 working,等你回答是 waiting,其余是 idle,没有 Agent 的普通终端是 shell。

7.3 加密通道

WebSocket 已连中继已转接Noise 握手中手机先发加密通道DATA / ACK协议可用hello 之后关闭4401 钥匙不对暂停发送手机没读完关闭手机落后太多 1013两条hello对不上未确认满 1 MiB收到 ACK中继缓冲满

流量控制是每部手机单独算的,一部手机卡住只影响它自己,不会拖慢电脑或其他手机。

7.4 手机上「未发送」的一句话

你写好一句未发送存在手机上发送中已送达从列表删除发送失败显示原因电脑在线且允许发送成功电脑报错你点重试

每条写操作带一个一次性 id,电脑记得做过哪些。所以网络闪断后重发,也不会把同一句话发两遍。

8. 手机能做什么,由角色决定

角色看会话和聊天答审批发消息、按键、开或停 Agent
只看 view能不能不能
能答审批 answer能能不能
完全控制 full能能能

权限只在电脑上检查。手机隐藏按钮只是为了好看,不算安全措施,因为手机和中继都被当作不可信。

9. 现在没有的,以及要注意的

项目现状
手机上的实时终端没有,这是 11-02 定的方向:手机只做聊天。有一个只读的「终端最后几行」,只能看和复制。等你回答时,问题下面有解析出的选项按钮和一个 Esc 按钮;审批另有允许和拒绝按钮。除此之外手机不能按键。
推送通知没接。需要 APNs / FCM 账号。App 在后台时连接可能断开,要回到前台才刷新,所以锁屏时不会收到「有会话在等你」。
电脑关机或后台没运行手机只能显示上次的内容,并标为离线。写好的话留在「未发送」。
聊天页的新消息靠每 2 秒轮询,不是推送,有 2 秒左右延迟。
iOS和安卓同一份代码,模拟器验过;真机完整走查还在待办里。

依据 Mooring 的代码(2026-11-07 读):mooring-remote/src/{secure,wire,link,close,role,protocol}.rs、mooring-relay/src/server.rs、mooring-host/src/{relay,remote}.rs、mooring-core/src/status.rs、mobile/src/remote/{client,hub,close}.ts、mobile/src/store/outbox.ts、mobile/src/remote/pump.tsx、docs/phone-app/spec.md。

// ON THIS PAGE 收起 展开目录
Silent Star约 5 分钟
文章目录9
Silent Star约 5 分钟