# 线程等待与状态读取:与 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)。