Files
codex-app-server/docs/thread-lifecycle-align.md
T
host cf08c4cf37 feat: 线程生命周期对齐 15 项(bindings/refs/快照/心跳绑定/移交落点)
对照 Codex app 第二批:GET /tasks/{id}/bindings(项目/
环境/主机/worktree/PR/附件/血缘总表)、/refs 外部引用
增删查、/worktree/archive 快照(branch/head/status/diff/
untracked 路径)与 restore(目录在才解归档、目录删了
409 附快照)、自动化 bind + heartbeat(走 thread/message
同款投递路,cron 绑定不唤醒)、移交 completed 后任务
worktree_host 跟随切换、pr/checks 响应补 head_sha、
thread/read 的 tool 条目补 server/status。

诚实边界见 docs/thread-lifecycle-align.md:已有 worktree
附加、自动化变化静默、建议器、服务端定时触发器未实现;
Memory 对位是待合的 PR #102,本批不重复实现。

三套件:cloud 948 OK(4 跳过)、host 91 OK、deploy 29 OK
2026-10-28 04:56:22 +08:00

215 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 与 Codex app 的功能对齐:15 项逐项对照
本批(feat-thread-lifecycle)之前已有 #103 的 8 项
(wait/read/message/handoff/worktree/pr-checks/automation/
sleep,见 docs/thread-wait-ops.md)。本文件逐项说明 15 项
在控制面的现状:已实现 / 本批补齐 / 待合 PR / 未实现。
“已实现”指端点真实存在且有单测,不是文档宣称。
## 1. Agent 线程管理
| 能力 | 落点 | 状态 |
|---|---|---|
| 创建独立线程 | POST /tasks | 已实现 |
| 从当前线程分叉,保留上下文 | POST /tasks/{id}/fork(终态任务;worker 调 host.py fork,分叉 thread 历史 + 拷 worktree 文件) | 已实现 |
| 继续执行暂停/空闲线程 | POST /tasks/{id}/continue(终态复活);POST /tasks/{id}/thread/message(终态 CAS 复活、进行中写后续) | 已实现 |
| 向其他线程发后续指令 | POST /tasks/{id}/thread/message;跨任务 POST /tasks/{id}/messages(peer,跨项目拒绝) | 已实现 |
| 读取状态、最近结果、历史摘要 | GET /tasks/{id}/thread/read;GET /tasks/{id}/thread?include_turns=1 | 已实现(#103) |
| 设置线程标题 | POST /tasks/{id}/thread/rename {name}(终态任务 + 本机线程) | 已实现 |
| 归档和恢复线程 | POST /tasks/{id}/thread/archive、/thread/unarchive(终态任务 + 本机线程,经 host.py CLI) | 已实现 |
| 查看主机、项目、工作目录 | GET /tasks/{id}/bindings(本批) | 本批补齐 |
注意:rename/archive/unarchive 走 `_task_thread_mutate`,
只允许终态任务且本机线程;docker 任务、进行中任务会
409/守卫拦截——这是线程正在变、快照不可信的保护,
不是缺口。归档只归档线程本身,worktree 快照见第 5 项。
## 2. 并行任务等待
wait_threads = POST /tasks/{id}/wait(#103):最多 8 个、
首个重要变化返回(完成/失败/需输入/需审批/需处理)、
超时回进度、afterCursor 只取新变化。事件等待机制,
不是固定间隔定时器;变化检测只读数据库(任务行、
审批表、后续表),不轮询 trace。
## 3. 线程状态读取
read_thread = GET /tasks/{id}/thread/read(#103):状态映射、
最近活动、改动文件、命令与退出码、待审批/待后续、
attention 汇总。本批补了一个小缺口:tool 条目原来只取
tool 名,丢了 server 与 status,现在拼成
`server/tool: status` 一行。`test_results` 仍为 null:
trace 里没有独立测试结果记录,不编“测试通过”。
## 4. 任务移交和远程主机切换
- 发起移交:POST /tasks/{id}/handoff {target_host}(#103)
- 查询进度/是否成功:GET /tasks/{id}/handoff(含 arrived)
- 对方主机上报:POST /tasks/{id}/handoff/{hid}/report
- 移交后继续发指令:POST /tasks/{id}/thread/message(与
主机无关,认领方按新主机判断)
- 本批补齐的落点:report 到 completed 时,任务的
worktree_host 改成目标主机——之前上报只改移交行,
任务行还指着旧主机,后续 /worktree、/pr/checks 会
按旧主机判断,这是真缺口,现已补上。
- 未实现:服务端不执行 SSH 迁移、不搬文件;迁移本身
由目标主机 worker 执行并回报(见 thread-wait-ops.md)。
## 5. Git Worktree 生命周期管理
| 能力 | 现状 |
|---|---|
| 创建独立 worktree | 已实现:worker 认领后同步建(host.py make_worktree,一任务一分支 host/<task>,非 git 仓库退化为目录拷贝) |
| 从指定 commit/分支/ref 创建 | 部分:任务绑 environment 时按 environment.branch 检出(environment 本身是异步创建过程,creating→ready/failed);任务直接建时一律从 HEAD 建,不接受 commit/ref 参数 |
| 将已有 worktree 附加到任务 | 未实现:worker 派发路径按任务 id 确定 worktree 目录,附加需要改派发,不在本批编一个只记关联、不被执行的“附加” |
| 查看创建进度 | GET /tasks/{id}/worktree(派生状态;创建是同步的,没有阶段流,不编百分比) |
| 恢复已归档 worktree | 本批:POST /tasks/{id}/worktree/restore(见下) |
| 归档不再需要的 worktree | 本批:POST /tasks/{id}/worktree/archive 拍快照(见下);真删目录仍是 merge/discard 的职责 |
| 保存未提交修改、未推送 commit、非忽略文件 | 本批快照记:branch、head_sha(即未推送 commit 的位置)、git status、git diff、untracked 路径清单。诚实边界:untracked 文件只记路径不拷内容,diff 只覆盖已跟踪文件;快照是证据,不是可一键还原的备份 |
| 保留 worktree 与线程的关联 | 已实现:快照行含 task_id + worktree_path;bindings 里 worktree 与任务同表返回 |
Restore 语义(不编造):目录还在时只把快照标 restored
(目录没动过,谈不上还原);目录已删时 409 并附快照
(重建需要仓库路径,服务端没有)。
## 6. 任务与 Git 状态绑定
本批 GET /tasks/{id}/bindings 一次返回:项目(名、
repo_url)、环境(名、分支、执行器、状态)、主机
(worktree_host、local/remote、本机名)、worktree
(状态、路径、分支)、分支、PR(url/number)、执行器
(任务值或全局缺省)、附件(文件 + 外部引用)、
worktree 快照清单、血缘(parent_task_id、forked_from、
coordinator_part)。之前这些字段散在任务行、项目行、
环境行、worktree 端点里,没有一张总表。
## 7. Pull Request 和 CI 诊断
read_pull_request_check_diagnostics =
GET /tasks/{id}/pr/checks(#103):checks JSON(workflow、
link、description)、失败 run 的 check-runs 日志原文
(截 4000 字)与行号注释(上限 20 条)。本批只补一个
字段:响应里加 head_sha(PR 当前 commit,之前内部
算出来只用于查 check-runs,没返回)。只读:不重跑、
不批准、不合并。外部 CI 状态即 check-runs 的
conclusion;构建/测试失败日志即 output.text。
## 8. 自动化、心跳和定时任务
| 能力 | 现状 |
|---|---|
| 创建/查看/修改/删除长期自动化 | POST/GET/PATCH /projects/{id}/automations、cancel(#103 前已有 + #103 的 update) |
| 按时间周期运行 | 已实现:interval_seconds(60 秒–30 天),到期判断 last_run + interval |
| 定期唤醒已有线程(heartbeat) | 本批:POST .../automations/{aid}/bind {task_id, mode: heartbeat} + POST .../automations/{aid}/heartbeat {message?}(缺省用自动化 prompt);投递走 thread/message 同款路(终态复活、进行中写后续、配额门同款) |
| cron(到期独立跑新任务) | 绑定 mode=cron 只作标记;heartbeat 口对 cron 绑定回 409(cron 不唤醒线程) |
| 复用已有对话上下文 | heartbeat 唤醒的是同一线程,上下文自然复用;cron 新任务不继承历史(与 fork 的语义一致) |
| 继续执行多日/多周任务 | 机制上可以(interval 上限 30 天 + 绑定线程长期存在),未做跨周的专项验证 |
| 只在变化/完成/失败/需处理时通知,状态不变保持安静 | 未实现:没有“与上次产出比对、无变化静默”的抑制逻辑;每次运行终态仍按现有推送通道通知 |
| 到期触发器 | 未实现(服务端本身不起定时器):触发需外部调 /heartbeat,或等 PR #101 的 run_due_automation 落地;本批只给了绑定与唤醒口,没宣称“已自动跑” |
## 9. 线程归档和恢复
线程归档/恢复见第 1 项(host.py CLI 透传)。归档时
保留的东西分两处:对话记录与线程状态在 codex-home
的线程存档里;worktree 快照在本批的
task_worktree_snapshots 里;PR 附件在任务行的
pr_url/pr_number 与 bindings 里;未提交文件快照即
第 5 项的快照(status/diff/untracked 路径)。长期
项目、阶段性任务、暂时暂停的 Agent 走“归档线程 +
拍 worktree 快照”两步,不是同一个端点。
## 10. 任务附件和交付物管理
- 文件:已实现——task_attachments(上传包绑定,任务行
attachments 字段,fork 时带走清单)。
- PR、worktree:已实现——任务行 pr_url/pr_number、
worktree 路径,bindings 同表返回。
- 生成的文档、构建结果、外部服务结果引用:本批——
POST/GET/DELETE /tasks/{id}/refs(kind: doc/build/
external/pr/file,只记引用不存内容、不校验链接可达)。
- 移除:refs 可 DELETE;文件附件的移除走上传包的
used 状态与任务删除,不在本批另开删除口。
## 11. 项目和主机上下文
GET /tasks/{id}/bindings 即上下文总表(项目、仓库、
主机、本地/远程、工作目录、默认执行器、环境、归属)。
项目默认执行器:任务 executor 为空时取全局缺省
(_default_executor),没有“项目级默认执行器”字段——
环境行(executor)是更细的默认,任务绑 environment
时以环境为准。
## 12. 插件、MCP 和外部连接器
已实现(经 host.py CLI 透传,项目 scope):
- GET /projects/{id}/skills(skills/list)
- GET /projects/{id}/apps(app/list)
- GET /projects/{id}/mcp-servers(mcpServerStatus/list)
- POST /projects/{id}/mcp-servers/reload
- POST /projects/{id}/mcp-servers/{name}/oauth-login
限制与官方一致:连接受账户、工作区、管理员权限、
插件安装状态限制;oauth-login 只是发起流程,真正
授权在宿主侧完成;容器内视图与宿主侧视图可能不同
(skills/MCP 端点 docstring 已注明)。GitHub、Slack
等具体外部系统在本系统是另外的连接器(GitHub App、
Slack webhook),不是 MCP 一个口。
## 13. Memory 和长期上下文
分两层,不要混淆:
- 已实现(本系统自有):KB(/projects/{id}/kb)与
任务 summaries——按任务/项目存的背景,不是跨任务
的用户长期记忆。
- 待合 PR:PR #102(feat-cloud-memory-identity,
user_profiles + identity_memories,用户级仅本人、
项目级项目成员可见,上下文各取最新 5 条)——
这才是官方 Memory 的对位。本批不重复实现,避免
与 #102 合并冲突;自动注入任务/thread 是 #102
之后仍待显式决策的下一步(注入点、去重、成本上限)。
- 可用性限制与官方一致:记忆的范围受账户与发布状态
影响,本系统里受项目成员权限限制。
## 14. 主动续作和建议性任务
未实现为独立服务。现有拼图:自动化(到期唤醒/独立
跑)+ triage(/projects/{id}/triage,自动化到期与
待处理汇总)+ #102 的记忆 + bindings 的 PR/任务状态。
“根据未完成任务、关联 PR、插件内容、Memory、项目
上下文建议下一步”需要一个建议器(读上述输入、产出
建议任务),不在本批范围;本批的 heartbeat 与
bindings 是给建议器准备的输入口与执行口,不是建议器
本身。不要把 triage 的到期计数说成“已会建议”。
## 15. 使用量和连接状态管理
已实现(不新开端点,本批只做对照):
- 账户使用量:GET /users/me/quota、GET /tasks/{id}/cost
- 限制窗口剩余:quota 的美元配额与无价 token cap
- 连接的主机:GET /workers(心跳与主机名)
- 远程主机是否可用:worktree_host + /workers 心跳龄
(_workers_heartbeat)+ /health(db_ok、worker_ok)
- App 更新状态:/health 的 commit 字段(部署提交)
- 已安装插件和连接状态:第 12 项的 skills/mcp-servers
- 项目和线程的运行环境:第 6 项的 bindings
这些用于任务调度与故障判断,不直接跑任务。
## 本批新增端点一览
- GET /tasks/{id}/bindings
- POST /tasks/{id}/refs、GET /tasks/{id}/refs、
DELETE /tasks/{id}/refs/{ref_id}
- POST /tasks/{id}/worktree/archive、
GET /tasks/{id}/worktree/snapshots、
POST /tasks/{id}/worktree/restore
- GET/POST/DELETE /projects/{id}/automations/{aid}/bind
- POST /projects/{id}/automations/{aid}/heartbeat
- GET /projects/{id}/automations/{aid} 响应加
heartbeat_binding 字段
- GET /tasks/{id}/pr/checks 响应加 head_sha 字段
- 移交 completed 上报后任务 worktree_host 跟随切换