Termexo v0.8.6 把重点放在了终端工作流最容易被忽略的一刻:页面刷新、重新连接,或者窗口尺寸发生变化之后,正在执行的任务还能不能接着工作。这个 MIT 开源的 Windows 多 Agent 工作台现在可以统一管理 Claude Code、Codex、OpenCode 和真实终端,并针对刷新后的 Agent 保留和终端画面恢复进行了改进。
对于长时间运行的编码任务来说,刷新浏览器不应该等价于重新启动进程。v0.8.6 的变化,正是把“前端视图重新加载”和“后端 Agent 生命周期”区分开来。
刷新不再误启动新的 Agent
过去,前端加载过程可能被当成一次新的终端启动。用户刷新页面时,应用没有先确认已有 Agent 是否仍然存在,就可能重新创建终端或重新启动任务。
这会带来几个直接问题:
- 正在运行的 Agent 可能被重复启动。
- 原有任务的上下文和输出连续性受到影响。
- 用户很难判断当前看到的是旧进程,还是刷新后新建的进程。
- 多 Agent 工作台中的状态会因为一次普通刷新而变得不可靠。
v0.8.6 的方向是让 Agent 进程独立于前端页面生命周期。页面刷新后,前端应当重新连接到已有进程,并通过进程标识、会话标识或其他持久状态判断该 Agent 是否已经运行,而不是把“没有本地 UI 状态”理解成“需要启动一个新 Agent”。
这是一条适用于终端型应用的通用原则:
UI 可以重建,任务进程不能因为 UI 重建而被隐式重启。
终端改用屏幕状态重放
本版还调整了终端恢复思路:重新连接或尺寸变化后,终端不再只依赖零散的输出文本,而是使用屏幕状态进行重放。
传统的输出回放通常类似于保存一段日志,再把日志重新写入终端。但终端并不是普通文本框。光标位置、清屏控制序列、颜色、换行和覆盖写入都会影响最终画面。同一段输出,如果在不同的终端尺寸或不同的光标状态下重放,结果可能并不一致。
屏幕状态重放更接近终端当前真正展示的内容:
- 保存当前屏幕网格中的字符和样式。
- 记录光标位置以及必要的终端属性。
- 连接恢复后,先重建屏幕,再继续接收实时输出。
- 尺寸变化后,根据新的终端尺寸重新计算显示状态。
这对交互式 Agent 尤其重要。Claude Code、Codex、OpenCode 或真实 shell 往往会更新同一行、刷新进度信息、切换界面区域。恢复时重建“屏幕长什么样”,比简单追加历史日志更符合用户实际看到的内容。
一个可落地的状态模型
下面是一个可以改造到终端前端或桌面工作台中的最小示例。它使用浏览器 sessionStorage 保存 UI 侧的 Agent 引用,并在加载时优先恢复现有会话。示例假设后端提供 GET /api/agents/{id} 和 POST /api/agents 接口;真正的进程管理仍应由后端负责。
<script>
const STORAGE_KEY = "termexo.active-agent";
async function getExistingAgent(agentId) {
const response = await fetch(`/api/agents/${encodeURIComponent(agentId)}`);
if (response.status === 404) return null;
if (!response.ok) throw new Error(`agent lookup failed: ${response.status}`);
return response.json();
}
async function createAgent(kind) {
const response = await fetch("/api/agents", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ kind })
});
if (!response.ok) throw new Error(`agent creation failed: ${response.status}`);
return response.json();
}
async function restoreOrCreateAgent(kind) {
const savedId = sessionStorage.getItem(STORAGE_KEY);
if (savedId) {
const existing = await getExistingAgent(savedId);
if (existing && ["running", "waiting", "idle"].includes(existing.status)) {
return existing;
}
}
const created = await createAgent(kind);
sessionStorage.setItem(STORAGE_KEY, created.id);
return created;
}
restoreOrCreateAgent("codex")
.then(agent => console.log("connected to agent", agent.id, agent.status))
.catch(error => console.error("agent restore failed", error));
</script>
这个示例的关键不在于 sessionStorage 本身,而在于启动条件:只有确认没有可复用的 Agent 时,前端才请求创建新进程。生产环境还应把 Agent 状态放在后端或桌面主进程维护,并处理多个窗口、应用重启、权限校验和僵尸进程清理。
终端画面可以采用类似的结构保存:
{
"agentId": "agent-42",
"cols": 120,
"rows": 36,
"cursor": { "x": 18, "y": 4, "visible": true },
"screen": [
{ "text": "Working on src/main.ts", "style": "default" },
{ "text": "Tests: 12 passed", "style": "success" }
]
}
这里的 screen 只是便于理解的简化表示。实际实现通常需要按行、按单元格保存字符、前景色、背景色、粗体和其他终端属性,并由终端解析器根据控制序列更新屏幕缓冲区。
这次更新对日常使用意味着什么
如果你经常让 Agent 执行持续数分钟甚至更久的任务,刷新页面时可以更放心地恢复原有工作现场。连接中断或窗口调整后,终端显示也更有机会保持与断开前一致,而不是出现重复输出、残留旧画面或布局错乱。
不过,状态恢复并不等于所有问题都自动消失。部署或改造类似系统时,仍需要明确以下边界:
- Agent 进程的真实所有者是谁,是后端服务还是桌面主进程。
- Agent 标识是否稳定,是否能防止刷新后误连到其他用户的进程。
- 进程已退出、崩溃或失去权限时,前端如何展示并决定是否重建。
- 终端尺寸变化发生在前端、PTY 还是 Agent 进程一侧,三者是否同步。
- 屏幕状态与实时输出之间如何排序,避免恢复快照覆盖新到达的数据。
采用建议
Termexo v0.8.6 的改动可以归纳为一份很实用的检查清单:
- 把页面生命周期和 Agent 进程生命周期分开。
- 刷新时先查找并连接已有进程,再考虑创建新进程。
- 为每个 Agent 保存稳定的会话标识和明确的状态机。
- 恢复终端时优先重建屏幕状态,而不是盲目追加输出日志。
- 对刷新、断线重连、终端缩放和进程退出分别编写测试。
- 在多窗口和多 Agent 场景下验证不会发生错误复用或重复启动。
对于终端工作台,这些变化不是表面上的刷新优化,而是对“任务是否连续”的一次修正。用户真正需要的是一个不会因为 UI 暂时重载,就忘记后台工作进度的工具。