feat: 任务间互发消息(API + agent 标记块,不中断任务)

同项目任务互发消息、对方做完可回消息:API POST /tasks/<目标>/messages
(from_task_id,目标项目 create_task + 发送项目 read 鉴权)与 agent
回合输出标记块(worker 回合后解析投递,不用联网、不用凭证,本地
与 Docker 都通)。投递不中断任务:对方运行中/排队中排成后续消息
(轮次边界执行,direct 优先);对方终态则续跑复活(resume_message +
queued),A→B→A 往返不受回合时序影响。拒绝项:自己、跨项目、goal
目标、已合并、复活位被占;每轮最多 5 块,仅 completed 轮次投递;
目标执行时配额门照常生效。控制台后续消息列显示发送方,新增给
其他任务发消息框(落已代理的 /tasks 下,不用改 nginx)。

测试:新增 test_peer_messages(15)、test_peer_messages_server(8)、
test_worker_peer(4);全量 cloud 654、host 64、deploy 19 全过(cloud 4 跳过)。
This commit is contained in:
host committed 2026-10-27 14:12:28 +08:00
1 parent 4901a1090b
commit ef30558f7c
11 files changed
+1171 -23

No files matched your search

+43
View File
@@ -512,6 +512,49 @@ goal 模式的继续:server 把任务转成新 goal 语义(goal=新消息、
合并任务 worktree 已删、请分支(merged 409);冲突与新建一致
(plan + approval=full、plan + goal 各 400)。
## 任务间互发消息(peer messages,Muse 式通用互发)
任意两个同项目任务可以互发消息、对方做完可以回消息,不
用经过用户中转。两个发送方:
- API:`POST /tasks/<目标>/messages`,body
`{from_task_id, body, mode}`(mode 缺省 queued);调用方
须对目标项目有 create_task、对发送任务项目有 read(防冒
名)。`GET /tasks/<任务>/messages` 列出该任务发出的消息
(收到的在 `/followups` 里,带 sender_task_id)。
- Agent:任务里跑的 agent 在回合输出里写标记块,worker 在
回合结束后解析并投递——agent 不用联网、不用凭证,本地
与 Docker 执行器都通。块格式见
`examples/peer-message-prompt.txt`;每轮最多 5 块。
投递语义(核心约束:互发消息不中断任务):
- 目标排队中 / 运行中:消息排成一条后续消息(带
sender_task_id),worker 排空循环在轮次边界执行——direct
优先、队内 FIFO,与用户后续消息同款,当前轮绝不被打断。
接收方 agent 看到的 prompt 带发送方前缀
`[来自任务 <id> 的消息]`,凭此知道回给哪个任务。
- 目标已终态(completed / failed / interrupted):消息复活
它——把带前缀的正文写进 resume_message 并把状态置回
queued,下次认领按续跑跑这一轮。没有复活,B 回给已结束
的 A 的消息会直接丢失,A→B→A 的往返没法成立。
- 一律拒绝(409 或投递层跳过,只记审计):发给自己、跨
项目、目标是 goal 任务、目标已合并、目标已有待续跑消息
(复活位被占)、目标不存在。
- 只有 completed 轮次的标记块被投递(失败 / 超时 / 取消 /
配额中断的轮次不投递);goal 任务的回合不解析标记块,且
目标是 goal 时直接拒绝——所以 goal 任务既不收也不发。
- 投递不查发送方配额(发送本身不花钱),但目标任务执行时
的配额门照常生效:排空前查配额,超限时该条回 pending、
任务走配额中断;复活任务认领时认领门同样生效。互发消息
不构成配额绕过。
- 边界:标记块正文不能含围栏(非贪婪匹配会把块截断成非法
JSON 而被跳过);worker 投递的 direct 消息只有排空优先
权,没有 API 直接发送那种“立即进链路”的标记(worker 侧
链路目录不一定在本机);复活投递的消息没有 followup 行,
发出列表里查不到,查审计 peer_message_sent 与目标的续跑
轮次;控制台“给其他任务发消息”框走 /tasks 下的
/messages,不用新增 nginx location。
## 测试