feat: 线程等待与状态读取 8 项(wait/read/message/handoff/worktree/pr-checks/automation/sleep)

对照 Codex app:wait_threads 首变+afterCursor+8上限、
read_thread 详情摘要、send_message_to_thread 复活失败任务、
handoff 登记与上报轮询、worktree 派生状态、PR checks JSON+
check-runs 日志与注释、automation GET/PATCH、clock.sleep
(API+host)。诚实边界见 docs/thread-wait-ops.md:无
worktree 阶段流、无 automation 变化静默、无独立测试结果
记录、handoff 服务端不执行迁移、wait 仅数据库级变化检测。

三套件:cloud 940 OK(4 跳过)、host 91 OK、deploy 29 OK
This commit is contained in:
host committed 2026-10-28 04:33:20 +08:00
1 parent 38c0797cb3
commit 755029f220
8 files changed
+2535

No files matched your search

+180
View File
@@ -0,0 +1,180 @@
# 线程等待与状态读取:与 Codex app 的对照
本批实现把 Codex app 的 8 个线程/等待类工具落到本控制面。
原则:只读的绝不写(不重跑、不批准、不合并),等待的绝不
创建任务或发指令;所有“状态”都来自已有记录(任务行、
trace、审批表、后续表、git 目录),没有的字段明说没有,
不编造。
## 三类
| 类 | 官方工具 | 本端点 | 说明 |
|---|---|---|---|
| 管理并行 Agent | wait_threads | POST /tasks/{id}/wait | 等一批线程的首个重要变化 |
| 管理并行 Agent | read_thread | GET /tasks/{id}/thread/read | 深入查看单个线程的摘要 |
| 管理并行 Agent | send_message_to_thread | POST /tasks/{id}/thread/message | 向已授权线程发后续指令 |
| 读取任务状态 | get_handoff_status | GET /tasks/{id}/handoff | 移交到另一主机后的进度 |
| 读取任务状态 | get_worktree_creation_status | GET /tasks/{id}/worktree | worktree 准备状态 |
| 读取任务状态 | read_pull_request_check_diagnostics | GET /tasks/{id}/pr/checks | PR 的 CI 诊断 |
| 持续监控/等待 | automation_update | GET/PATCH /projects/{id}/automations/{aid} | 长期自动化的查看与修改 |
| 持续监控/等待 | clock.sleep | POST /tasks/{id}/sleep,host sleep | 一次性等待后继续 |
配套:POST /tasks/{id}/handoff 与 POST /tasks/{id}/handoff/{hid}/report
是移交的登记与上报口(官方是“先发起 handoff 再轮询”,
本批把发起与上报也补齐,否则 GET 无数据可轮询)。
## 对照表
### wait_threads — POST /tasks/{id}/wait
- 入参:`task_ids`(可选,缺省取锚任务的 coordinator 子任务),
`timeout_seconds`(缺省 30,上限 120),`after_cursor`(可选)。
- 一次最多 8 个线程,超了回 400(不静默截断)。
- 只返回第一个发生重要变化的线程:完成/失败等终态、
需要用户输入(elicitation 审批)、需要审批(其它审批)、
需要继续处理(待后续消息)。变化判断按 task_ids 顺序,
不是按真实时间戳排序。
- 无变化时超时回当前进度(progress 全量 + timed_out=true)。
- `after_cursor`:上一次返回的 cursor 只含各任务的状态计数,
不含正文;带它再等时,已报过的变化不再重复报。
- 它不创建任务、不发指令,只等和收集状态。
- 诚实边界:变化检测只读数据库(任务行、审批表、后续表),
不轮询 trace 文件;跨任务顺序是调用方给的顺序。
### read_thread — GET /tasks/{id}/thread/read
- 返回:状态映射(queued→waiting,completed/merged→completed,
failed/merge-failed→failed,interrupted→interrupted)、
线程摘要、最近活动(trace 末 10 条)、改动文件(trace 里
kind=file 的路径去重,上限 50)、执行命令(kind=exec 的
命令与退出码,末 20 条)、待审批、待后续、Agent 链、
阻塞/需要决策/需要处理/需要指令的 attention 汇总。
- 线程取不到时 thread=null + note,仍回 200(与 /thread 的
409/502 不同:读摘要不该因为线程缺失而全单失败)。
- `test_results` 恒为 null + 说明:trace 里没有独立的测试
结果记录,测试只能从命令退出码与 Agent 消息里看,不编
“测试通过”。
- docker 执行器任务没有本机线程,thread=null(这是预期,
不是故障)。
### send_message_to_thread — POST /tasks/{id}/thread/message
- 入参:`body`(必填,上限 20000 字),`mode`(queued|direct,
缺省 queued)。
- 授权模型:API 侧要求 API key + 项目 create_task 权限,
并记审计 thread_message_sent;“用户在聊天里让我协调”
本身不是 API 权限,调用方必须持 key。
- 排队/运行中任务:写入 task_followups(pending),direct
模式同时追加一条直接 trace。
- 已完成/失败/中断的任务:不是 409,而是复活——用 CAS
把状态改回 queued、清空旧结果与旧错误、写入 resume_message;
若已有待复活消息或状态已变,回 409(不双重复活)。
- 已合并任务回 409:worktree 已删,应 fork 新任务而不是复活。
- 目标任务(goal/verify)回 409:目标任务不接受后续消息。
- 发给其它任务的消息需要用户明确授权;本任务内按用户指令
协调。跨项目发消息不在本端点(走 peer_messages 的
/messages,跨项目会被拒绝)。
### get_handoff_status — GET /tasks/{id}/handoff
- 移交状态:pending / accepted / running / completed / failed;
终态不可再变,同态重报只算进度更新;进度 0–100,不因
终态强制改成 100。
- 同一任务同一时间只能有一个未关闭移交,否则建移交回 409。
- 响应含 current_host 与 arrived(当前主机是否等于目标主机):
这是“到了”的证据。
- 诚实边界:服务端只登记意图与轮询上报,不执行 SSH 迁移、
不搬文件;实际迁移由目标主机的 worker 执行并回 report。
### get_worktree_creation_status — GET /tasks/{id}/worktree
- 状态:remote(worktree 在别机)/ ready(目录 + .git)/
present_no_git / creating(排队或运行中且目录未建)/
missing(终态且目录未建;已合并任务删 worktree 是设计,
不是丢失)/ unknown。
- 本机且已就绪时附带当前分支(git branch --show-current,
失败则 null,不阻塞整单)。
- 诚实边界:worktree 创建在本系统是同步的,没有可观测的
“接收对象 / 更新文件”阶段流,不编百分比。
### read_pull_request_check_diagnostics — GET /tasks/{id}/pr/checks
- 只读:不重跑检查、不批准、不合并。
- 两层取数:`gh pr checks --json`(workflow、link、description
即哪个 Action 失败与失败摘要),JSON 失败时回落制表符解析;
有仓库 + head sha 且有失败项时,再走 check-runs API 拿
失败日志原文(output.text,截 4000 字)与文件行号注释
(annotations:path / start_line / message,上限 20 条,
失败 run 上限 5 个)。
- check-runs 一层失败时不 502 整单:checks 本身已够回答
“哪个检查失败”,此时 check_runs 回 available=False + note。
- 无 PR 时 409;worktree 在别机时 409(gh 必须在 worktree
所在主机跑;诊断是只读拉取,不排队等对方 worker)。
### automation_update — GET/PATCH /projects/{id}/automations/{aid}
- GET 回定义 + 近期运行(运行取不到时 runs=[] + note,
不 502 定义本身)。
- PATCH 只接受 name / prompt / interval_seconds / repo_url,
其它键静默忽略;无可更新字段回 400;已取消的自动化
回 409(取消是终态,更新不能复活);同项目同名冲突回 400。
- 更新不重置 last_run_at:改间隔只影响下一次到期,绝不
触发一次立即运行。
- 诚实边界:“变化才通知”未实现:每次运行终态仍按现有
worker 推送通道通知,没有“与上次输出比对、无变化静默”
的抑制逻辑。
### clock.sleep — POST /tasks/{id}/sleep 与 host sleep
- API:read 权限;seconds 缺省 5,只接受 0–60 的数,超出
回 400(API 层不静默截断);睡的是调用方的等待,不暂停、
不加速、不改动任务本身;返回时重读任务状态。
- host:`host.py sleep --seconds N`,0–60,超了退出码 1;
Ctrl-C 打印已睡秒数 + interrupted=true,退出码 130。
## 10 步协调流程(映射到端点)
1. 主 Agent 明确需求、影响客户端、验收标准
→ 写进 POST /tasks 的 prompt(目标任务用 verify_command)。
2. 为 Android、iOS、macOS、Web 或后端分别派发子任务
→ POST /tasks,parent_task_id 指向主任务(coordinator)。
3. 每个子任务在独立 worktree 或明确分支中开发
→ 任务 worktree 由 worker 分配;GET /tasks/{id}/worktree 查。
4. 使用 wait_threads 等待任务完成或遇到阻塞
→ POST /tasks/{主}/wait(缺省即等 coordinator 子任务)。
5. 使用 read_thread 查看详细结果
→ GET /tasks/{子}/thread/read。
6. 对失败任务使用 send_message_to_thread 要求修复
→ POST /tasks/{子}/thread/message(失败任务会复活续跑)。
7. 汇总各客户端改动并检查共享 API、事件和数据结构
→ 各子任务的 read_thread 摘要 + result_text 人工/主 Agent 对照。
8. 运行跨客户端构建、测试和集成验证
→ 新建验证任务(或目标任务的 verify_command),再走 4–6。
9. 读取 PR 的 CI 诊断并继续修复
→ POST /tasks/{id}/pr 建 PR 后,GET /tasks/{id}/pr/checks,
失败项用 thread/message 发回修复。
10. 最后由主 Agent 汇总变更、证据和剩余风险
→ 各任务 result_text + trace + 本对照表的诚实边界。
## 派发模板(映射到 POST /tasks 字段)
- 任务:prompt 首行写清任务名与一句话目标。
- 客户端:prompt 内写明(Android / iOS / macOS / Web / 后端),
本系统没有独立的客户端字段,不要另建字段。
- 仓库:项目 repo_url(建项目时定);子任务不另设仓库。
- 基线提交:写进 prompt(如“基线 abc123”),或 environment
的分支字段;没有独立的基线字段。
- 要求:写进 prompt(复用现有组件、不新建导航页等约束)。
- 验收:写进 prompt;目标任务另用 verify_command 做自动验收。
- 返回:任务完成时 result_text + trace;read_thread 再给出
改动文件、命令与退出码、待处理项。主 Agent 按“改动文件 /
执行命令与结果 / 未解决问题”三项核对,不只看一句“已做完”。
## 未实现 / 未宣称
- worktree 创建阶段流(接收对象、更新文件百分比):无。
- automation 的“无变化静默”抑制:无。
- 测试结果独立记录:无(只有命令退出码与 Agent 消息)。
- handoff 的实际迁移执行:服务端不做,靠目标主机 worker 上报。
- wait 的 trace 级变化检测:无,只有数据库级变化。
- 自动注入身份/记忆上下文到任务:不在本批(见 identity-memory)。