WIP: 任务附件——host seed-dir 与上传模块(未完,server/worker/console 待续)

This commit is contained in:
host committed 2026-10-27 04:29:40 +08:00
1 parent c102327040
commit 9537e6f0d2
2 files changed
+212 -3

No files matched your search

+39 -3
View File
@@ -1560,9 +1560,32 @@ def dispatch_from_branch(server, state, name, prompt, repo, dep_branch,
return task, ""
def _seed_worktree(cwd, seed_dir):
"""把暂存附件拷进 worktree(dispatch/goal 共用,turn 开始前)。
seed_dir 是服务端写好的暂存目录(只含常规文件,无符号链接):
顶层 .git 与符号链接一律跳过;子目录整树拷入,同名文件覆盖
(worktree 刚从 repo 建出来,覆盖即用户文件优先)。"""
import shutil
seed_dir = os.path.abspath(seed_dir)
if not os.path.isdir(seed_dir):
raise RuntimeError(f"seed-dir 不是目录: {seed_dir}")
for entry in sorted(os.listdir(seed_dir)):
if entry == ".git":
continue
src = os.path.join(seed_dir, entry)
dst = os.path.join(cwd, entry)
if os.path.islink(src):
continue
if os.path.isdir(src):
shutil.copytree(src, dst, dirs_exist_ok=True, symlinks=False)
else:
shutil.copy2(src, dst)
def dispatch(server, state, name, prompt, repo, model, effort, sandbox,
approval_policy, use_worktree=True, parent_task=None,
depends_on=None, priority=0, max_retries=0):
depends_on=None, priority=0, max_retries=0, seed_dir=None):
if name in state["tasks"]:
raise RuntimeError(f"task 已存在: {name},先换名或 discard 它")
if repo and not os.path.isabs(repo) and not os.path.exists(repo):
@@ -1575,6 +1598,13 @@ def dispatch(server, state, name, prompt, repo, model, effort, sandbox,
branch = None
if use_worktree and repo:
cwd, branch = make_worktree(repo, name)
if seed_dir:
# 附件只能进 worktree:直接以 repo 为 cwd 时拷进去等于污染
# 仓库,必须 loud fail,绝不静默放行。
if not use_worktree or not repo or not cwd:
raise RuntimeError(
"seed-dir 需要 worktree,当前派发没有建 worktree")
_seed_worktree(cwd, seed_dir)
thread_id = server.create_thread(cwd, model=model, sandbox=sandbox,
approval_policy=approval_policy)
task = {"name": name, "thread_id": thread_id, "cwd": cwd,
@@ -1905,6 +1935,8 @@ def main():
p.add_argument("--name", required=True)
p.add_argument("--task", required=True)
p.add_argument("--repo", default=None)
p.add_argument("--seed-dir", default=None,
help="附件暂存目录:派发时拷进 worktree 再跑 turn")
p.add_argument("--no-worktree", action="store_true")
p.add_argument("--depends-on", default=None,
help="逗号分隔的依赖任务名,依赖未完成前不跑")
@@ -1948,6 +1980,8 @@ def main():
p = sub.add_parser("goal")
p.add_argument("--name", required=True); p.add_argument("--task", required=True)
p.add_argument("--repo", default=None)
p.add_argument("--seed-dir", default=None,
help="附件暂存目录:派发时拷进 worktree 再跑 turn")
p.add_argument("--verify", default=None,
help="验收命令;不传时走模型自决:模型在回合末尾"
"单独一行输出“目标已达成”即达标")
@@ -2277,7 +2311,8 @@ def main():
args.approval_policy,
use_worktree=not args.no_worktree,
depends_on=deps, priority=args.priority,
max_retries=args.max_retries)
max_retries=args.max_retries,
seed_dir=args.seed_dir)
# 生产教训:先落盘任务登记再跑 turn,中途被杀/超时后
# 任务还在 host-state 里,可用 run 续跑;否则 turn 中断
# 就等于任务丢了(2026-10-25 委派评审 300 秒超时实测)。
@@ -2538,7 +2573,8 @@ def main():
elif args.cmd == "goal":
task = dispatch(srv, state, args.name, args.task, args.repo,
args.model, args.effort, args.sandbox,
args.approval_policy)
args.approval_policy,
seed_dir=args.seed_dir)
save_state(args.state, state)
run_goal_loop(srv, state, args.state, task, args.task,
args.verify, args.max_rounds, args.model,