背景报告 · OMNIGENT 架构与 Agent 控制 · 2026-07-19 · commit 固定审计

Omnigent 怎么控制各个 Agent?
不是一招,是四种 harness 模式

Omnigent 是 Python(FastAPI) 的分层控制面:Server → Host → Runner → Harness。它驱动底层编码 Agent 的方式不是单一的——有自己跑 agent loop 的 headless/SDK、有 native app-server(结构化协议)、有 ACP,还有最有特色的 native TUI:把厂商真实 CLI 跑在一个 tmux pane 里,靠 send-keys 注入输入、靠 forwarder 镜像 transcript。它确实内置了"伪终端",但用的是 tmux,不是 Paseo 那种 node-pty(因为它是 Python 不是 Node)。

直接
回答
有内置伪终端,但是 tmux 不是 node-pty。 Omnigent 为每个 native TUI harness 起一个独立 tmux server(私有 socket),把 claude/cursor/hermes/kiro 等真实 TUI 跑在里面,再用 PTY↔WebSocket 桥把画面投给你。不是全走 ACP——ACP 只是四种模式之一(Goose 用)。Codex/OpenCode 走 app-server 结构化协议,还有一类是 Omnigent 自己直接调模型 API 的 headless loop
01

分层拓扑:Server / Host / Runner / Harness

会话控制面(Server+SQL)是真相之源;执行下沉到 Host→Runner;每个会话一个 harness 子进程。

CLIENT Web / PWA / Electron iOS·Android = WebView 薄壳 CONTROL PLANE Omnigent Server FastAPI · REST / SSE · auth policy · presence · 会话树 SQL + artifact store conversation/items·ACL·cost REST/SSE 你的机器 / VM / 沙箱 Host daemon 常驻 outbound WS Runner 每会话一个 Python 子进程 tmux · sandbox · MCP · policy per-conversation Harness 子进程 UDS(POSIX)/ TCP loopback(Win)· 懒启动 · 空闲回收 HARNESS 之下:四种驱动方式(见 §2) ① Headless / SDK Omnigent 自己的 agent loop 直调模型 API(llms 适配层: openai/anthropic/gemini/ bedrock/vertex/databricks) 无 CLI、无终端 ② Native app-server Codex / OpenCode 起厂商 headless app-server 走结构化 JSON 协议 (与 Multica codex 路径类似) ③ Native TUI(tmux) claude·cursor·hermes·kimi· kiro·goose·qwen·antigravity·pi 真实 TUI 跑在 tmux pane send-keys 注入 + forwarder 镜像 transcript ← 你问的"伪终端"在这 ④ ACP Agent Client Protocol Goose 等 标准化 JSON-RPC 会话
出处:omnigent/serveromnigent/hostomnigent/runner/app.pyruntime/harnesses/process_manager.py("one subprocess per AP conversation ... per-conversation Unix socket")。
02

四种 harness 模式:它怎么"驱动"一个 Agent

模式 ①

Headless / SDK loop

  • Omnigent 自己就是那个 agent
  • 直接调模型 API(omnigent/llms/*)
  • 工具调用、循环都在 Omnigent 进程内(inner executor)
  • 可用 Python entry-point 插件扩展
  • 没有厂商 CLI、没有终端
模式 ②

Native app-server

  • Codex(codex_native_app_server.py)
  • OpenCode(opencode_native_app_server.py)
  • 起厂商的 headless server 进程
  • 结构化 JSON 协议收发事件
  • 不需要伪终端
模式 ③

Native TUI(tmux)

  • claude/cursor/hermes/kimi/kiro/goose/qwen/antigravity/pi
  • 把厂商真实交互 CLI 跑在 tmux pane
  • 输入:tmux send-keys(或 antigravity/pi 用 connect-RPC)
  • 输出:*_native_forwarder.py 镜像 transcript
  • 你能实时 attach 看画面
模式 ④

ACP

  • Agent Client Protocol
  • Goose 等(test_goose_acp_e2e)
  • 标准化 JSON-RPC 会话
  • 与 Multica/Paseo 的 ACP 是同一套标准
关键:"支持某个 Agent"在 Omnigent 里可能对应不同模式。比如 Goose 既有 native TUI(goose session)也有 ACP;Codex 走 app-server。所以它不是"统一用 ACP",而是按 Agent 逐个挑最合适的接入方式——代价是每个厂商一大坨专属适配代码(*_native_forwarder.py 动辄几万行)。
03

Native TUI 内幕:tmux 注入 + transcript 镜像 + PTY 桥

浏览器 xterm.js 你看到/操作的终端 拥有字符网格/滚动/选择 PTY↔WS 桥 ws_bridge:fork tmux attach, 屏幕→二进制 WS 帧;resize→ioctl control_bridge:tmux -C 行协议 Runner 私有 tmux server 独立 socket,一终端一 server pane 里跑厂商真实 TUI socket+target 记进 tmux.json 可选 bwrap/seccomp/egress 隔离 厂商 CLI TUI claude / cursor / hermes / kiro … 输入路径(你发消息) Web 消息 → tmux send-keys 打进 pane(antigravity/pi 改用 connect-RPC) 输出路径(agent 回你) *_native_forwarder / _bridge / hook 解析 TUI 输出 → 结构化 transcript 落库
inner/terminal.py 原话:"Terminal environment: managed tmux sessions ... Each terminal instance runs a command in its own tmux server (isolated socket) with optional filesystem isolation (fork) and sandboxing (bwrap/seccomp)."
两种 attach 桥:ws_bridge.py(fork tmux attach,渲染屏幕→二进制 WS 帧,resize 走 ioctl(TIOCSWINSZ))与 control_bridge.py(tmux -C 控制模式,%output 行协议,浏览器 xterm.js 拥有网格,输入用 send-keys -H 十六进制逐字节注入)。
为什么用 tmux 而不是自己写 PTY:tmux 天生就是"可分离/可重连的持久终端会话 + 多路复用",一个 pane 既能被 forwarder 程序化读取、又能被你 tmux attach 实时接管,还能同机直连或跨机走 WS 桥。Omnigent 用它把"人类才会用的交互式 TUI"变成可托管、可镜像、可共享的会话。
04

所以:伪终端有,但是 tmux,不是 node-pty

PaseoOmnigent
语言TypeScript / NodePython
伪终端实现node-pty + @xterm/headlesstmux(每终端独立 server/socket)
前端终端xterm(RN/web)xterm.js(web)
远程投屏紧凑二进制 WS 帧PTY↔WS 桥 或 tmux -C 控制模式
给 agent 喂输入写 PTY / SDKtmux send-keys(或 connect-RPC)
读 agent 输出PTY 流 / SDK 事件per-厂商 forwarder 解析 TUI → transcript
是否只有终端一条路否(也有 SDK/ACP)否(headless SDK / app-server / TUI / ACP 四条)
一句话:两家都给你一个"能远程接管的真实终端",思路一样;实现不同——Paseo 是 Node 生态的 node-pty,Omnigent 是 Python 里直接托管 tmux。而"控制 agent"这件事,Omnigent 比 Paseo 铺得更宽:同一个平台里,一个 agent 可能走 headless、app-server、tmux TUI 或 ACP 中的任意一种。
05

三家 Agent 控制底座对比

维度MulticaPaseoOmnigent
语言GoTypeScriptPython
主接入方式每 task 起 CLI 子进程
(stream-json / app-server / ACP)
保活 provider 会话
(SDK / ACP)+ node-pty
四模式:headless / app-server / tmux TUI / ACP
内置伪终端无(解析结构化事件流)node-pty 常驻tmux(每终端独立 server)
跑厂商真实 TUI 并镜像可投终端,但不"伪装"结构化 transcript:send-keys 注入 + forwarder 镜像
进程模型每聊天轮次重启 + --resume长期保活会话每会话一个常驻 harness 子进程(懒启/空闲回收)
维护代价Go adapter,中等adapter 较干净最重:每厂商专属 native 适配(forwarder 数万行)
放到我们的语境:Omnigent 和 Multica 同属"云控制面 + 受管执行"一派,但 Omnigent 在接入底层 Agent 上走得比 Multica 激进——它不满足于结构化协议,还去托管并镜像厂商的真实交互 TUI(tmux)。这带来"原生 TUI 也能进共享会话/policy/fork"的上限,代价是巨大的、需要持续追厂商版本的适配面。Multica 每轮重启 CLI 的做法更省心但交互延迟更高;Paseo 的 node-pty 常驻更即时但偏个人本地。