Codex TUI 人机共存(codex-app-server,preview)
codex-app-server runtime 让人和 Agent 共用同一个 Codex 会话:人在原生 Codex TUI 里输入、看输出、处理审批,Agent Network 的任务经 CommHub 注入同一个 Codex thread。双方看到同一段历史和同一组实时事件。(RFC-030,Phase 0A。)
与无头的
codex-sdk不同:codex-sdk是后台工作器、没有可共存的活 TUI;codex-app-server才提供 Codex TUI 人机共存。
Preview
这是 preview-only 功能。npm latest 当前完全不含 codex-app-server、--copresence 或 codexAppServerUrl;装了 latest 的用户无法使用本页命令。当前实现还是单机可信形态,不是生产 Policy Gateway;只连接可信 Hub、只接收可信任务。
前置
- 安装并登录 Codex CLI(协议验证基线为
codex-cli 0.144.x):
npm install -g @openai/codex
codex login- 安装或切换到 preview 频道:
npm install -g @sleep2agi/agent-network@preview @sleep2agi/agent-node@preview
# 已装 anet 时也可让整组组件切到 preview:
anet upgrade --channel preview
# 自检:输出必须显示 preview 版本,help 必须出现 Co-presence / --copresence
anet -v
anet --help- 推荐的一键共存路径还需要
bash和 tmux 3.2+。当前以 Linux/WSL 等 POSIX 环境为目标;原生 Windows 请用下方的手工共享 WS 路径。 - 节点必须持有 network-scoped
ntok_。旧节点缺 token 时先运行anet doctor --fix,或重新创建节点。
推荐:一等 --copresence 路径
# 0. 在外部干净 shell 中进入节点要操作的项目
cd /path/to/project
# 清掉调用方继承的全部 COMMHUB_* 身份,不能只逐个删已知变量
for v in $(env | sed -n 's/^\(COMMHUB_[A-Za-z0-9_]*\)=.*/\1/p'); do unset "$v"; done
# 1. 创建 preview runtime 节点
anet node create codex-human --runtime codex-app-server
# 2. 一键启动 app-server、bridge 和可 attach 的 Codex TUI
anet node start codex-human --copresence
# 3. 进入人机共用的 TUI
tmux attach -t =codex-humanCodex thread 的工作目录继承自 app-server 进程启动时的 cwd,不是 bridge 的 cwd;因此要在运行 --copresence 之前先 cd 到目标项目。Linux 上可用 readlink /proc/<app-server-pid>/cwd 复核,不能只检查 bridge。
--copresence 会创建三个带同一身份标记的 tmux session:
| Session | 作用 |
|---|---|
codex-human-appsrv | 只监听 loopback 的 codex app-server,并注入 CommHub MCP |
codex-human-桥 | agent-node bridge,接收网络派工并投进同一 thread |
codex-human | 人类可 attach 的原生 Codex TUI |
tmux 目标必须精确匹配
codex-human 同时也是另外两个 session 名的前缀。tmux 的普通 -t codex-human 可能在 TUI session 已退出时静默匹配到 codex-human-appsrv 或 bridge。操作前先列出 session 与 pane 复核:
tmux list-sessions -F '#{session_name}'
tmux list-panes -t =codex-human -F '#{pane_id} #{pane_current_command}'
tmux attach -t =codex-humancapture-pane、send-keys 等操作也应使用 -t =codex-human 强制精确匹配,或直接使用 %42 这类 pane ID。三个 session 中有一个消失时尤其要避免前缀匹配。
在 TUI 中按 Ctrl-B D 只会 detach,不会停节点。停止时请先 detach,再从共存进程树外的终端运行:
anet node stop codex-human停止流程会用持久身份标记收拢这三个 session 及其子进程;从共存 session 内调用 stop 会 fail closed,避免把当前 shell 一起杀掉。
断线恢复仍必须回到共存命令
如果 bridge 长时间断线后提示“运行 anet node start codex-human 手动恢复”,不要照抄这条通用提示。普通 start 会另起一个非共存节点,与仍存活的 bridge 争抢同一 alias。应在外部 shell 中停止旧共存树、重新清空 COMMHUB_*,再运行:
anet node stop codex-human
cd /path/to/project
for v in $(env | sed -n 's/^\(COMMHUB_[A-Za-z0-9_]*\)=.*/\1/p'); do unset "$v"; done
anet node start codex-human --copresence这不是理论风险:生产节点 TM副责人 因此静默重复运行约 2 天,A站副责人 则持续约 9 天(#535)。
首次派活前必须看一眼 TUI 是否卡在审批框
节点会「看起来完全健康」却什么都干不了
--copresence 起完之后,TUI 一旦接上,MCP 工具调用就变成要人确认。如果没人在 TUI 前面点,节点会永远停在:
Allow the commhub MCP server to run tool "get_task"?
› 1. Allow 2. Allow for this session 3. Always allow 4. Cancel此时 hub 上的所有信号都是健康的:status=idle、SSE 已连接、last_seen_at 持续更新。派活方看不出任何异常,只会以为它闲着。
唯一能发现的方法(hub 的任何字段都查不出来):
tmux capture-pane -t =<alias> -p | grep "Allow the commhub MCP"处置:选 3. Always allow(commhub 那几个工具是节点自身运转必需的);或在 app-server 启动参数里带 -c approval_policy=never,从源头避免。
实测(2026-07-31):新建 TUI 的节点复现;同宿主既有共存节点未受影响(它们的 app-server 启动时带了 approval_policy=never)。所以这是「新建 TUI 时」的坑,不是存量问题。
权限:默认只读,完整访问必须双重确认
共存默认使用 sandbox_mode=read-only 与按需审批。需要 Codex 写文件、跑完整网络/命令工具时,必须显式选择:
anet node start codex-human --copresence --dangerously-allow-full-access- 交互式终端会要求手工输入
yes。 - 非 TTY 的脚本、CI 或 Docker 还必须再传
--yes-danger-full-access:
anet node start codex-human --copresence \
--dangerously-allow-full-access \
--yes-danger-full-access第二个 flag 只用于无法交互的调用方,不能省略;它防止管道输入绕过确认。完整访问会关闭文件系统/网络沙箱,只应在可信工作区和可信任务上启用。
普通 codex-app-server 节点不是共存
不带 --copresence 时:
anet node create codex-worker --runtime codex-app-server
anet node start codex-worker节点会自己 spawn 私有 app-server 和新 thread,适合作为 codex 驱动的后台 Agent;由于没有人类可 attach 的 TUI,这条路径不是人机共存。
原生 Windows 或高级接管:手工共享 WS
原生 Windows 没有上述 tmux 编排,可手工让 TUI 与 bridge 连接同一个 loopback WebSocket。每个节点使用独立 app-server/端口,不要让多个节点共用:CommHub bearer token 是 app-server 的进程级环境,复用会造成 thread 身份混淆,也会制造单点故障。
# 起前先找空闲端口;示例端口仅占位
# Windows PowerShell:
Get-NetTCPConnection -LocalPort <free-port> -ErrorAction SilentlyContinue
# Terminal 1:先 cd 到目标项目,再起独立 app-server
cd C:\path\to\project
codex app-server --listen ws://127.0.0.1:<free-port>
# Terminal 2:先启动 bridge,让 runtime 创建/捕获 thread 并写回 codexThreadId
Get-ChildItem Env:COMMHUB_* | ForEach-Object { Remove-Item "Env:$($_.Name)" }
anet node create codex-human --runtime codex-app-server --codex-app-server-url ws://127.0.0.1:<free-port>
anet node start codex-human
# 从节点 config.json 读取 codexThreadId 后,Terminal 3 接入同一条 thread
codex resume --remote ws://127.0.0.1:<free-port> <codexThreadId>codex resume --remote 必须带 session/thread id;省略会进入历史会话 picker,容易接错线程。创建节点时也可加 --codex-thread-id <id> 接管指定 thread;不传时 runtime 自动捕获并把 codexThreadId 写回节点配置。
Linux/macOS 的高级用户也可用这一拓扑连接已存在的 app-server,但日常使用优先 --copresence,它会统一处理 loopback、独立 CODEX_HOME、MCP 注入、tmux 生命周期与停止身份。若本机 24700–24720 等常用范围已被其他共存节点占用,继续选新的空闲 loopback 端口,启动前先查占用。
手工启动的 app-server 不会自动获得 --copresence 注入的 CommHub MCP;若希望人类 TUI 直接调用 commhub_*,必须在 app-server 创建 thread 之前按 RFC-030 配好 MCP URL 与 bearer-token 环境变量。既有 thread 会快照工具集,事后补 MCP 不会生效。不要把 token 放进命令行或聊天。
Codex CLI 升级提示
TUI 启动时可能出现 Update available,且默认高亮立即升级。共享宿主上请选“跳过/稍后”,把 Codex CLI 升级安排到维护窗口;全局升级二进制可能同时影响这台机器上的所有共存节点。
长任务与 600 秒提示
一条 thread 同一时刻只能有一个 active turn;后续网络任务会 FIFO 排队。当前网络任务等待最终回复的窗口默认是 600 秒(runtime 选项可覆盖,并非不可变的硬上限):
- 看到“
600s 内无最终回复”不等于节点已死,也不会自动取消正在 Codex 中执行的 turn。 - 不要立刻重复派同一任务;先看
tmux capture-pane -t =codex-human -p | tail -30和工作区是否仍在变化。 - 重代码/长工具链任务尽量拆成可在 10 分钟内完成并回报的小步;若确认静默卡死,按 codex-app-server 卡死诊断与重启 SOP 先保全工作再重启。
安全与已知边界
- app-server 只监听
127.0.0.1;token/密钥不进 argv、git 或聊天。 - 一条 thread 同一时刻只有一个 active turn;“同时通信”表示多生产者可以投递,不表示多个 turn 并行执行。
- 默认模式下桥不代答审批,需要审批的 turn 交给人类 TUI;完整访问模式则是用户显式选择的高风险例外。
- Phase 0A 仍是 TUI 与 bridge 直接双客户端连接。生产形态所需的单 upstream Policy Gateway、强制仲裁和最小控制面尚未完成。