T3 Code 拆解:一个控制面怎么同时指挥 Claude Code 和 Codex

pingdotgg/t3code,TypeScript 写的 MIT 项目,2026 年 2 月 8 日建仓,九个月涨到 40216 stars、9017 forks,今天还在推 nightly。它把自己定义为 “agent harness control surface”:一个跑在你开发机上的 server,加上 iOS、Android、web 和 Electron 四端客户端,让你用手机或浏览器远程指挥本机已经登录的 Claude Code、Codex、Cursor、Grok Build、OpenCode、Google Antigravity、fx 七个编码 agent。

一句话判断:如果你已经在用两个以上的编码 CLI,并且确实有“离开工位后继续盯着 agent 跑任务”的需求,T3 Code 是目前这类控制面里完成度最高、架构最干净的一个。但它自己在 README 里写着 “We are very very early. Expect bugs.”,4152 个 open issues,而且基本不接受外部 PR。

先想清楚一个问题:你是需要一个更好的 agent,还是需要一个指挥现有 agent 的遥控器?T3 Code 做的是后者。

本文提纲

  • 它是什么:先排除三个误解
  • 为什么需要它:终端里的 agent 被绑在椅子上
  • 怎么用:三分钟装好,手机接管
  • 总体架构:执行永不离开环境
  • 核心机制一:事件溯源的 v2 编排器
  • 核心机制二:checkpoint、线程结算与 turn 完成的分离
  • 远程连接:身份与路由解耦的安全设计
  • provider 适配层:只报告引擎真实能做的事
  • 真正的创新点
  • 横向比较
  • 边界、成本与风险

它是什么:先排除三个误解

第一个误解:它是一个新的编码 agent。不是。T3 Code 自己不含模型,不替你写代码。真正执行的是你机器上的 provider 进程——Claude Code 的 CLI、Codex 的 CLI、Cursor CLI 等等。T3 是这些进程的“驾驶舱”:启动、发消息、看流式输出、批准权限、切换模型、管 PR,全部通过一个统一界面完成。README 里列的灵感来源是 Codex desktop app、Conductor、Claude Desktop 和 Cursor Glass,但它认为没有一个达到它的标准:性能、远程就绪、真正开放。

第二个误解:它是个 Electron 套壳。不是。Electron 只是四端之一。架构上桌面端内嵌一个 server,但它的 renderer 和手机、网页遵守同一条边界:只能通过认证过的 RPC 控制环境,永远不能拿客户端自己的文件系统或凭证替环境做决定。仓库结构是标准 monorepo:apps/ 下是 server、desktop、mobile、web、marketing 五个应用,packages/ 下是 contracts、client-runtime 和每个 provider 一个适配包。

第三个误解:开源 = 开放协作。这里有个微妙的区别。代码 MIT 协议随便 fork,README 甚至明说“如果我们走错方向,希望你拥有 fork 出自己想要的编辑器所需的一切”。但贡献政策是“基本还不接受贡献,小修复可以考虑,大功能不会收”。feature request 走 Discussions 的 Ideas 分类。这是 pingdotgg(Theo 的 ping Labs,也就是 t3 栈那帮人)的典型打法:开源代码,闭门开发节奏。介意这一点的人需要提前知道。

为什么需要它:终端里的 agent 被绑在椅子上

使用 Claude Code 或 Codex 超过一个月的人,大概率都遇到过这几个场景:

agent 跑一个长任务,你不敢关电脑,终端窗口不能关,人离开工位后它弹了权限请求,十分钟后回来看见它卡在第一行批准上;你同时用 Claude Code 和 Codex,但它们各有各的终端、各有各的会话、各有各的登录态,切来切去;晚上在沙发上想看看下午那个重构跑完没有,只能打开 VPN 连回机器,对着一个移动端体验为零的终端;agent 自己 gh pr merge 之后,UI 里的线程状态和真实 PR 状态对不上。

问题的本质是:编码 agent 的执行被绑死在“一台机器上的一个交互式终端”里。agent 自己已经很能干了,但人这一侧的控制点只有一个 TTY。T3 Code 要做的是把控制点从 TTY 解放出来:执行还在你的机器上(这是安全和订阅的双重要求——你的 Claude/Codex 订阅登录态在本地),但控制面变成任何设备上的一个 app。

这也解释了它为什么坚持 BYO CLI 而不是 BYO API key:你用的是已有的订阅,请求不经过 pingdotgg 的服务器;server 跑在你本机,移动端只是连回这个 server。

怎么用:三分钟装好,手机接管

命令行安装一行:

curl -fsSL https://t3.codes/install.sh | sh

Windows 用 PowerShell:

irm https://t3.codes/install.ps1 | iex

之后运行 t3 启动 server 并打开本地 web app,t3 service install 注册成后台服务(无头机器也能跑),t3 update 升级。不想装可以 npx t3@latest 试一次。桌面端分发渠道全得离谱:winget、scoop、Homebrew cask、.deb、AUR(stable 和 nightly 两个包,PKGBUILD 在仓库 packaging/aur 里),甚至已经进了 nixpkgs 的 nixos-unstable。

前提条件是至少装好并登录一个 provider,比如 codex login 或 claude auth login。Antigravity 例外,不需要 CLI,在设置里装并 Google 登录即可。

远程访问有四种路:直连(LAN)、Tailscale、SSH、以及官方的 T3 Connect 中继(需要从互联网访问时用,比如让云端的 ChatGPT 通过 MCP 连你的机器)。手机装上官方 app,配对一次就能在地铁上批准 agent 的权限请求。

日常使用里有两个值得单独说的功能。

权限模式分四级,在输入框里按线程切换,mod+shift+a 快速打开:Supervised(命令和文件改动都问)、Auto-accept edits(文件改动自动放行)、Auto(用 provider 自己的自动审查)、Full access(全放行)。新线程默认是 Full access——和 OpenMausBot 一样的激进取舍,企业管理员可以用 managed policy 设上限。

Triage an issue 是个很有 dogfooding 味道的功能:在命令面板选它,会开一个无项目线程,让你自己装的编码 agent 去读 T3 server 的日志和状态、对照该版本的 T3 源码、查已知 issue,最后帮你写一个高质量的 GitHub issue,应用修复或发帖前会问你。app 起不来时还能在终端跑 npx t3 triage。用 T3 自己支持的 agent 来诊断 T3,这个套娃设计在工程上是成立的:排障所需的信息(日志、状态、对应版本源码)本来就在这台机器上。

总体架构:执行永不离开环境

docs/internals/overview.md 第一段就把边界钉死了:执行保留在拥有工作区的那个环境里,web、桌面、移动客户端通过认证 RPC 控制它;远程客户端永远不能用自己的文件系统、provider 凭证或机器状态替代环境的。

MERMAID_BLOCK_0

边界两边的归属划分得很清楚:provider 进程、终端、Git、项目文件归 server;连接和领域状态归共享的 packages/client-runtime,web 和手机共用,防止重连和多环境行为在不同客户端分叉;客户端只提供平台能力和 UI。

RPC 契约在 packages/contracts/src/rpc.ts,它是独立版本化的客户端与 server 之间的边界。两个细节:一是订阅只发客户端正在看的那个线程需要的状态,看一个线程不用为所有线程的历史付费;二是“socket 通过认证”不等于“这个 socket 上每个方法都授权”,环境级鉴权单独一层。

核心机制一:事件溯源的 v2 编排器

这是整个仓库技术含量最高的部分,在 apps/server/src/orchestration-v2/。

v2 编排器(Orchestrator.ts)对命令做串行化处理,但只负责“决定产生什么事件”,不碰 provider,也不碰文件系统。EventSink.ts 在一个数据库事务里提交四样东西:事件、持久化投影(projection)、命令受理回执、outbox 副作用。订阅者在事务提交之后才能收到事件。副作用由 EffectWorker.ts 在意图落库之后执行,再把结果喂回编排器。

这个顺序解决了 agent harness 里最恶心的一类 bug:状态不一致。命令重试天然幂等(回执已经在了);投影永远不可能跑到事件日志前面;你收到“命令已受理”时,含义精确地等于“意图已提交”,而不是“provider 已经干完了”。外部 I/O 全部被挡在命令决策和数据库事务之外。

还有一个容易忽略的故障恢复规则:绑定在已丢失 provider 进程上的副作用不能简单重放——比如那个进程可能已经把消息发出去了。恢复流程会先妥善处理这些孤儿副作用,再接纳新工作。持久化事件还要保证老版本数据永远可解码:改 schema 影响的不只是在线 RPC 流量,还有启动时的历史回放。

核心机制二:checkpoint、线程结算与 turn 完成

checkpoint 用隐藏 Git ref 实现。 快照工作区但不在你的分支上加 commit;回滚时必须同时协调工作区状态和 provider 的对话状态——如果 provider 不能把对话也回滚,T3 会在动文件系统之前直接拒绝这次回滚(CheckpointRestoreSafety)。这条规则值得划重点:很多工具的“快照”只快照了磁盘,回滚后 agent 的上下文还停留在旧世界,接下来写的东西全是错的。

turn 结束和后续工作结清是两个里程碑。 RunFinalizationService 独立记录 provider turn 和 run 的状态:迟到的 checkpoint 或 diff 不能延长已记录的 provider 耗时,也不能让客户端一直显示“provider 工作中”。

线程结算是 server 侧自主完成的,不依赖有没有客户端连着。 ThreadSettlementService 按 PR 和不活跃设置评估,客户端只渲染持久化结果,不许用本地时钟或自己的 PR 缓存推算状态。这里有个非常实战的补丁:如果 merge 发生在 T3 之外(比如 agent 自己跑了 gh pr merge),没有通知可收,于是 PullRequestSyncReactor 在一个执行过 merge/close 命令的 run 结束后,重读该线程关联的 PR;每个 forge 的定义文件里用 changeRequestCloseCommand 写明各自 CLI 的对应命令。

远程连接:身份与路由解耦

多设备远程控制是 T3 的主战场,这部分设计(docs/internals/remote.md)的安全细节密度很高。

环境有一个跨重启、跨端点变化保持不变的 ID;保存的连接只存在客户端本地。一个环境可以挂一串有序路由(直连 LAN、Tailscale、T3 Connect),客户端按顺序试第一个能通的。直连路由先用公开描述符探测,防止你保存的局域网地址在另一个网络上被陌生机器应答时把凭证送出去。连通之后 supervisor 还会对更优路由做预检热切换,不稳定的路由冷却,避免在烂网络上反复横跳。

配对 token 的一条规则很能体现威胁建模:HTTPS 下配对的 token 永远不走明文 HTTP,即使后来学到了一个明文地址也不行。无头机器上后台服务没有配置 host 时监听所有 IPv4 网卡,但只接受对端双地址都是 loopback、私网或 tailnet 的连接——这样 VPS 的公网地址保持关闭,包括公网 IP NAT 到私网网卡的云主机场景。

除此之外还有 peer link:环境 A 的 agent 可以通过 /mcp 以外部 MCP 客户端身份登录环境 B,拿配对码授权,B 只当多了一个可随时吊销的 OAuth 客户端。终端里的 Claude Code、云端的 ChatGPT 这类“T3 没启动的 agent”也走同一套 MCP 入口,默认只读,想发消息、开线程要逐项授权。

provider 适配层:只报告引擎真实能做的事

七个 provider 的能力差异巨大,T3 的纪律写在 docs/internals/adding-a-provider.md 里,我认为这是全仓库最值得抄的工程文档。

核心原则一句话:编排和客户端只读能力位,永远不读 provider 名字。新 provider 的验收标准不是“能跑一轮对话”,而是“T3 的每个功能在它上面都表现诚实”。

具体规则包括:provider 不支持 fork、rollback、steering、subagent,就把对应能力位关掉,编排器自动退回到可移植方案。比如线程 fork 在原生不支持时退化为文本式 context handoff——把历史压缩成摘要交接,每条文本项最多 240 字符(237 字符加省略号),并且文档明确写着“不宣称与 provider 原生上下文等价”。权限模式同理:provider 没有原生自动审查,就不许提供 Auto,严禁 T3 自己代答审批来“模拟”这个模式,原话是“T3 的检查比 agent 自己的执行弱”。服务端发现一个线程存着当前 provider 不支持的模式,按 Supervised 运行。

协议接入也分了两档:符合 ACP 规范的 agent 直接作为 ACP Registry 条目接入,零特殊处理;只有规范覆盖不到的行为(Grok、Antigravity)才给专用驱动,而且是共享 ACP adapter 上的一个小 flavor,禁止在通用 registry adapter 里加 agent id 判断。原生协议(Codex、Claude、Cursor、OpenCode、Pi、Muse)则实现 ProviderAdapterV2 接口。

账号隔离是另一个重点:driver kind 标识集成,instance 标识一套配置和账号生命周期,同一种 provider 的两个账号不共享会话和模型目录。Antigravity 的例子最极端:强制文件型凭证存储(否则 macOS keychain 条目会在实例间共享),启动环境里清掉环境自带的 Google 凭证,防止一个实例静默用了另一个账号的账单。

测试方法也值得说:用录制脚本从真实 provider 录下对话转录,通过真实编排器、adapter 和投影回放,所有 provider 共用一组场景(simple、multi_turn、queued_turn、turn_interrupt、message_steering、resume、各类 tool_call 审批)。手写帧的单测只用于纯逻辑,因为“手写的协议帧是对协议的猜测”。

还有一个小处见真章的规则:provider 更新只允许通过“可证明拥有该二进制”的安装器进行——解析路径跟完符号链接,证明是 Homebrew 的 keg 或 npm 的全局目录才调对应的包管理器,证明不了就用 provider 自带的 updater(claude update、codex update 等)。否则 T3 可能在 PATH 上的另一个 Node 旁边装出第二个副本。

真正的创新点

第一,把“控制面与执行面分离”做成了产品级架构,而不是一个远程终端。 它不是把 TTY 转发到手机(那是 SSH 干的事),而是把 agent 状态建模成独立领域:线程、turn、checkpoint、PR、结算、权限,全部是 server 端持久化的一等公民,客户端随时断开重连。事件溯源加多环境路由,让“手机上点一下批准”背后是和桌面端完全相同的语义。

第二,“诚实能力位”这套 provider 适配纪律。 多 agent 聚合工具最常见的烂法是:界面上七个 provider 看起来功能一样,实际有三个的按钮点下去是静默失败或降级模拟。T3 把“不支持就关、不伪装、退回到明确更弱的可移植方案”写成了验收标准,连权限模式都不许代答。能力差异不是 UI 的麻烦,是数据模型的一部分。

第三,checkpoint 同时覆盖磁盘和对话。 隐藏 Git ref 快照加上“provider 不能回滚对话就拒绝文件回滚”的硬约束,把 agent 状态回滚这个行业老大难按成了一个原子操作。配合 server 自主的线程结算和对 T3 外部 merge 的同步 reactor,状态可信这件事是端到端的。

横向比较

方案 定位 provider 远程 执行位置
T3 Code agent 控制面 七个 CLI/引擎,统一界面 四端 + LAN/Tailscale/SSH/T3 Connect 你的机器
终端里的 Claude Code / Codex agent 本体 单 provider 无(靠 SSH/mosh 凑) 你的机器
Codex desktop / Claude Desktop 官方桌面壳 单家 弱 你的机器
Conductor / Cursor Glass 单家或少数几家的图形前端 绑定厂商 部分支持 你的机器
OpenMausBot 聊天形态的 agent 团队 claude/codex/grok CLI 桌面互联 你的机器 + 云电脑

和官方桌面壳比,T3 的价值是 provider 中立和真远程;和 OpenMausBot 比,两者都走 BYO CLI,但形态和重心不同:OpenMausBot 把 agent 做成聊天联系人、多 bot 编排和云电脑,面向“派活给一支队伍”;T3 面向开发者自己的工作流,线程直接绑定 Git 仓库、worktree、PR 和 checkpoint,它连 t3.json 都支持 worktree 创建时自动跑脚本。一个是团队协作产品,一个是 IDE 外的开发控制台。

它放弃的东西也明确:不提供模型,不提供算力,没有云端沙箱,所有价值都建立在“你自己有订阅、有一台常开的机器”之上。对不用这些 CLI、或者只用 ChatGPT 网页版写代码的人,它没有意义。

边界、成本与风险

成熟度是第一条。版本还在 0.1.7 nightly,每天出构建,4152 个 open issues,README 自己喊 expect bugs。nightly 通道意味着行为和界面随时变,生产关键工作流别只押它一个控制点。

贡献模式是第二条。MIT 保证你能 fork,但主线功能不接受社区大 PR,路线图握在 pingdotgg 手里。好消息是架构文档里“走错方向就 fork”不是空话,契约和适配层边界清晰,真 fork 时有东西可接。

权限默认值是第三条。Full access 作为新线程默认,意味着 agent 的命令和文件改动默认不拦——虽然每家 provider 自己还有一层(比如 Claude 在 bypass 模式下遇到危险删除仍会拒绝并提示安全改写),但建议新用户先在 Supervised 下跑几天。企业环境务必配 managed policy。

钱的部分很轻:软件免费,没有云账单(T3 Connect 中继的具体限额需要看官方说明),成本就是你已有的各家订阅和一台机器。真正的隐性成本是学习面:四端、环境、路由、线程、checkpoint、七种 provider 各自的行为差异表,它不是装好就无感的工具。

最后给个上手路径:本机 curl 装好后先只接一个 provider,web app 里把默认权限改成 Supervised,跑两个真实任务感受线程和 checkpoint;再装手机 app 走 Tailscale 配对(别先碰公网中继),体验离开工位批准请求;等这些顺了,再考虑接第二个 provider 和开 T3 Connect 给云端 agent 用。它最值钱的部分不在第一眼的 UI,而在你第三次在手机上点下“允许”时,背后那条事件、投影、权限和路由全链路都是对的。

参考文档与链接

你会把 agent 的默认权限设成 Full access 还是 Supervised?评论区聊聊你的放权节奏。


作者: itech001
来源: 公众号:AI人工智能时代(the-ai-era)
网站: https://www.theaiera.top/
关注每日最新AI新闻和技术博客,主页有更多的文章的AI 技术参考:https://www.theaiera.top

本文首发于 AI 人工智能时代,转载请注明出处。

posted @ 2026-11-02 22:13  iTech  阅读(10)  评论(0)    收藏  举报