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

+13 -1
View File
@@ -20,6 +20,8 @@ import shutil
import subprocess
import sys
from cloud.peer_messages import extract_peer_messages as _extract_peer_messages
MAX_STDOUT_CHARS = 2000
@@ -224,6 +226,7 @@ class LocalExecutor(Executor):
stdout = _truncate_stdout(stdout_full)
status = "failed"
usage = None
task_messages = []
if returncode == 0:
status = "completed"
# JSON 解析必须用完整 stdout:截头后的前 2000 字在
@@ -242,6 +245,10 @@ class LocalExecutor(Executor):
# 原样透传给 worker 记 actual;脏数据由
# _clean_usage 挡掉,不进 actual。
usage = _clean_usage(parsed.get("usage"))
# 互发消息标记块:从完整解析出的 text 里提取(截头
# 版 text 尾部的块会丢),worker 在 completed 轮次
# 后投递;goal 路径不走这里,goal 任务只收不发。
task_messages = _extract_peer_messages(parsed.get("text"))
else:
host_status = None
else:
@@ -258,6 +265,7 @@ class LocalExecutor(Executor):
# 的旧结果 dict 不含此字段,保持不变。
"stderr": _truncate_stdout(getattr(result, "stderr", "")),
"usage": usage,
"task_messages": task_messages,
}
def _host_name(self, task):
@@ -1231,11 +1239,13 @@ def _parse_host_result(task_id, fake):
status = "failed"
host_status = None
usage = None
task_messages = []
if returncode == 0:
status = "completed"
# 与 _result_from_runner 同理:JSON 解析用完整 stdout,
# 否则长 text 会把 host 的 failed 误判成 completed,
# 也会丢掉 JSON 末尾的 usage。
# 也会丢掉 JSON 末尾的 usage。标记块同样从完整解析
# 出的 text 里提取。
try:
parsed = json.loads(stdout_full)
except (TypeError, ValueError):
@@ -1245,6 +1255,7 @@ def _parse_host_result(task_id, fake):
status = ("completed" if host_status == "completed"
else "failed")
usage = _clean_usage(parsed.get("usage"))
task_messages = _extract_peer_messages(parsed.get("text"))
return {
"executor": "docker",
"task_id": task_id,
@@ -1254,6 +1265,7 @@ def _parse_host_result(task_id, fake):
"host_status": host_status,
"stderr": _truncate_stdout(getattr(fake, "stderr", "")),
"usage": usage,
"task_messages": task_messages,
}