Ta
基于 Cell 与 pi 的 coding agent。ta 就是改名为 Ta 的 pi:交互界面、-p、--mode json|rpc、斜杠命令、会话树、扩展、Skills、pi 包都与 pi 一致;另外多了这些:
- 默认用 codemode:模型通过
codemode写 JavaScript 脚本调用所有工具,可以并行调用、在脚本里过滤大输出,只有脚本的输出进上下文(见下文「pi 补丁」)。 - 会话锁:同一个会话文件同时只能被一个 Ta 进程写(pi 没有锁,两个进程会交错写入)。
- Ta 的外观与身份:终端界面启动时显示 Cell 的像素小恐龙、版本和常用按键(按展开键看全部按键;
quietStartup: true时不显示);system prompt、提示文字、MCP 客户端名都说 Ta;/bug在浏览器里打开 Ta 的 GitHub issue(带版本、模型、平台,不带别的);去掉了依赖 pi 官方服务(Radius)的/share;/changelog显示 Ta 的更新记录。pi 面向 pi.dev 的版本检查和安装统计默认关闭(TA_SKIP_VERSION_CHECK=0、TA_TELEMETRY=1可打开)。pi 的环境变量在 Ta 里叫TA_OFFLINE、TA_SELF_UPDATE_COMMAND、TA_TELEMETRY,原来的PI_*名字也照样生效。 ta serve:把 pi 会话通过 WebSocket 提供给任意多个客户端(Web、桌面、手机),并自带 Web 客户端,见 @celljs/ta-host 与 @celljs/ta-web。
安装
npm i -g @celljs/ta # Node.js >= 22.19
ta # 交互模式
ta -p "总结这个仓库" # 一次性运行与 pi 的对应
| pi | Ta | |
|---|---|---|
| 全局目录 | ~/.pi/agent/ | ~/.ta/agent/(auth.json、models.json、settings.json、sessions/、extensions/ …,内容格式相同) |
| 项目目录 | .pi/ | .ta/;项目说明仍是 AGENTS.md |
| 环境变量 | PI_CODING_AGENT_DIR | TA_CODING_AGENT_DIR、TA_CODING_AGENT_SESSION_DIR |
| 子命令 | install remove update list mcp | 相同,另加 serve、exec-server、pair、relay |
pi 的扩展和 pi 包可以直接用:ta install npm:<包名>。想复用项目里已有的 .pi/skills,在 .ta/settings.json 里写 "skills": ["../.pi/skills"]。
装了 Cua Driver 时,Ta 能在后台使用这台电脑上的桌面应用,见内置扩展 @celljs/ta-computer-use。
每轮回复改动了文件时,Web 客户端在输入框上方显示改动的文件和增删行数,点文件在工作台查看差异,也可以一键撤销这一轮的改动,见内置扩展 @celljs/ta-changes。
让 Ta 按时干活:/loop 30m <要做的事>、/remind in 45m <要做的事>,或者直接说"每天 9 点汇总昨天的提交"。Ta 服务到点在新对话里运行,Web 客户端的"定时任务"页列出任务和每次运行的总结,见内置扩展 @celljs/ta-cron。
Ta 和 pi 一样,默认不在工具调用前请求批准;需要的话安装可选包 @celljs/ta-permission-gate。
ta serve
ta serve # 127.0.0.1,上次的端口(被占用时换一个空闲端口)
ta serve --host 0.0.0.0 --port 4100
ta serve --web # 同时在浏览器里打开 Web 客户端(已带上 token)
ta serve --exec ws://box:4200/exec # 工具在另一台机器的 ta exec-server 上执行(TA_EXEC_TOKEN)
ta exec-server --host 0.0.0.0 --port 4200 --cwd /workspace
ta pair # 配对手机等设备,经 Relay 连回这台电脑(默认 https://relay.cellbang.com)
ta relay --port 8080 # 自己运行一个 Relay启动后把地址和访问 token 写进 ~/.ta/agent/serve.json(权限 0600)。Web 客户端就在同一地址的 /:页面文件不含数据,无需 token;调用(/api)都要 token。--web 把 token 放在 URL 的 # 片段里(不会发给服务器,也不进 Referer),页面读到后存进 localStorage 并从地址栏去掉。端口和 token 跨重启保持不变(记在同目录的 serve-state.json,权限 0600;删掉它就换新的 token 和端口),这样装成应用(PWA)的 Web 客户端下次打开还能找到 ta serve 并自动登录;--port 指定端口时以它为准。TA_SERVE_TOKEN 可指定 token(例如由桌面壳生成),这时不记录它。客户端用 @celljs/ta-client 连接。
客户端没指定目录时,对话在 ta serve 的工作目录里打开:默认是启动它的目录,Web 客户端和桌面应用里可以改("打开工作目录…"),记在 agent 目录的 ta.json。模型服务可以在 Web 客户端里登录(齿轮菜单的"模型账号…",与终端的 /login 写同一个 auth.json)。对话也可以在另一台机器上工作,像 VS Code 的 Remote SSH:在目录选择器里选一台主机(~/.ssh/config 里的 Host,或 user@host),目录就是 ssh://<host>/<path>。Agent 和模型凭据留在 ta serve 这台机器,命令、文件、终端、git 在那台主机上;ta serve 用本机的 ssh 连过去(密钥、ssh-agent、ProxyJump 照常可用,要密码或确认新主机时页面会弹窗),第一次会把一个很小的服务器(和没有时的 Node.js 22)放到那台主机的 ~/.ta/server。那台主机上的开发服务器在内置浏览器里打开 localhost 地址即可,端口自动转发。见 ta-remote-ssh.md。
ta pair 配对的设备经 Relay 连到 ta serve,内容端到端加密;Web 客户端的"在手机上使用…"做同样的事,并显示二维码。
构建
bun run build 先用 cell build --root serve 把 ta serve 的服务端打成 dist/serve/index.mjs、Web 客户端打成 dist/public/(serve 应用装了 @celljs/ta-web 就会构建前端,@celljs/serve-static 负责托管),再由 scripts/assemble.ts 把 ta 命令(ta-harness 的 TaCli)打成 dist/cli.mjs,并生成 dist/pi/:一个把 pi 改名为 Ta 的 package.json(piConfig),加上 pi 的主题、HTML 导出模板、docs 和 examples。ta 启动时让 pi 从这个目录读取品牌信息(PI_PACKAGE_DIR),和 pi 官方二进制的目录布局相同。Ta 自带的 pi 包(TaPackages.BUILTIN)构建后复制到 dist/pi/packages/<名字>,不含源码与测试,可在 Web 客户端的 设置 → 包 关闭。
ta 运行时用到的都在 dist/ 里,所以发布的包没有 dependencies,装 ta 不会再装 Cell 和 pi;构建用到的包都是 devDependencies。ta serve 的服务端是 serve/ 下单独的 Cell 应用(私有,不在 workspaces 里):Cell 只从 dependencies 收集组件,它的 serve/package.json 列出这些组件,@celljs/ta 自己就不必有 dependencies。给 ta serve 加组件时,两处都要加(serve/package.json 的 dependencies 决定打包内容,package.json 的 devDependencies 决定 turbo 的构建顺序)。
ta 命令本身不加载 Cell,启动时间和内存与 pi 相同;只有 ta serve 加载 Cell。
pi 补丁
pi 里写死的品牌文字、/bug 和 /share 两个内置命令,以及 codemode 的默认值,用补丁改:
- 补丁是仓库根目录的
patches/@earendil-works%2Fpi-coding-agent@<版本>.patch,由bun run pi-patch(scripts/pi-patch.ts,见下)生成,登记在根package.json的patchedDependencies,bun install时自动打上。只改 pi 的打包产物dist/bundle(Ta 只运行它)。 - 包管理器的补丁只在本仓库生效,所以构建时
scripts/assemble.ts把打过补丁的dist/bundle复制进dist/pi/dist/bundle随包发布(约 9 MB);ta设置PI_PACKAGE_DIR指向dist/pi,PiBundleUtil就加载这一份。进程里只有这一份 pi,扩展通过它内置的模块表拿到同一个实例。没打补丁时构建直接失败。 - 打包产物运行时从 node_modules 加载的
jiti(加载扩展)、@silvia-odwyer/photon-node(图片缩放)和quickjs-wasi(codemode 的 WebAssembly)也由scripts/assemble.ts从 pi 旁边复制进dist/pi/node_modules,版本与 pi 相同;扩展用到的typebox和 pi 自己的包走打包产物内置的模块表。构建时会扫描打包产物里按名字加载的包,出现不在这份清单(或可选清单)里的就失败,升级 pi 时照提示补上。
改了什么,以 scripts/pi-patch.ts 的 REPLACEMENTS 为准(按组列出每处原文和替换文字):
| 组 | 改动 |
|---|---|
| codemode by default | 默认工具加上 codemode,codemode.mode 默认 only:模型只看到 codemode 一个工具,read / bash / edit / write、扩展工具和 MCP 工具都在脚本里用 tools.<name>() 调用。pi 没有给宿主的默认设置层,只能改这里。改回 pi 的行为:settings.json 写 "codemode": { "mode": "on" }(其他工具也直接提供)或 "defaultTools": ["-codemode"](不用 codemode),单次运行用 --tools -codemode |
| built-in commands | 去掉 /share(上传到 Radius)和 pi 的 /bug(发给 pi 的开发者);/bug 由 Ta 的扩展提供;崩溃后的 /bug 提示随之改写 |
| system prompt | 开头改为 Ta;pi 文档一节说明 pi 的 ~/.pi/agent、.pi/、pi 命令在 Ta 里是什么 |
| help of ta | ta -h、ta update -h、ta auth -h 里的 pi → Ta;环境变量列为 TA_*(由 TaBrand.activate 转给 pi 的 PI_*),去掉 Ta 自己设置的 PI_PACKAGE_DIR 和随 /share 去掉的 PI_SHARE_VIEWER_URL;示例里的 MCP 服务 radius 换成 github;ta update ta 与 ta update self 同义 |
| update of ta | ta update(self、ta)向 npm 查询 @celljs/ta 的最新版本,而不是向 pi.dev 查询(那里返回的是 pi 的包名,会把 Ta 换成 pi);不提示运行 pi 的安装脚本(Ta 是单文件打包,没有要固定的依赖) |
| messages | tmux 设置提示、项目信任提示、ChatGPT 登录端口占用提示:pi → Ta;MCP 客户端名 pi → ta |
只改 pi 没有设置、参数或扩展点可用的地方;能用 piConfig、环境变量(TaBrand.activate)或 Ta 的扩展做到的,不进补丁。补丁不手改:pi 的打包产物按哈希命名分块、压缩成长行,手改的补丁在 pi 升级后必然失效。
升级 pi 时:改 ai-packages/ai-coding-agent 等处的 pi 版本并 bun install,然后在 apps/ta 下 bun run pi-patch。脚本从 Bun 缓存里取未打补丁的 pi,按 REPLACEMENTS 逐条替换(不管文件名,每条原文在整个 dist/bundle 里须恰好出现约定的次数),再用 bun patch --commit 生成新版本的补丁、去掉 .bun-tag-* 段并删除旧版本的补丁文件;最后把根 package.json 的 patchedDependencies 里旧版本那一行删掉,跑 bun run test。某条原文在新版 pi 里变了,脚本会列出它找到的次数并停下,改那一条的 from 再跑。bun run pi-patch --check 只检查不生成;test/pi-patch.spec.ts 也会检查当前安装的 pi。
没有改的:pi 文档正文(system prompt 已告诉模型如何对应)、Radius 登录选项的介绍(那是 pi 官方的服务,文字属实)、设置页"Pi's drawn cursor"和包管理器的一条错误信息(在几十 KB 的长行里,补丁代价大于收益)。
性能基准
bun run bench # 除长时间场景外全部运行,约 3 分钟,输出 Markdown 表格
bun run bench streaming # 指定场景;--help 列出全部场景基准用本机构建产物(先 bun run build),模型是独立进程里的假 OpenAI 兼容服务(bench/fake-model.ts:rate 首 token 200 ms、每秒 100 个 token;burst 尽快输出 5000 个 token)。对照组是 pi --mode rpc:pi 要服务多个会话只能每个会话一个进程。内存与 CPU 从进程外用 ps(Linux 读 /proc)采样,不干扰被测进程。
参考结果(Apple M4 Pro,Node.js 24):
| 场景 | pi | Ta |
|---|---|---|
--version / -p | 0.12 s 112 MB / 0.18 s 127 MB | 0.12 s 114 MB / 0.19 s 128 MB |
| ta serve 空载 | — | 66 MB,空闲 CPU 0 |
| 32 个会话 | 每会话一个进程,约 127 MB × 32 | 181 MB(每会话约 1.6 MB) |
| 单会话流式(rate)首 token / CPU | 209 ms / 5% | 209 ms / 5–6% |
| 10 个会话同时流式 | 1693 MB,4.57 s CPU | 191 MB,1.92 s CPU |
| 50 个客户端看同一会话 | — | 每客户端每 delta 约 35 µs |
endurance(长时间场景,需显式指定)在 64 MB 堆上限下连续 60 轮突发输出,检查内存是否收敛。
测试
bun run test 是集成测试:真实构建产物 + 本地假的 OpenAI 兼容模型服务,覆盖 print / JSON 模式、会话锁、安装 pi 包与权限拦截,以及 ta serve 的多客户端广播、断线补发、审批先到先得、幂等回执、会话锁、空闲回收和限流。