feat: agent definition v2 (registry, persistence, developerInstructions channel)

Registry: POST /tasks accepts agent_name, loading <name>.json
from repo-root agents/ (traversal-guarded, skill-pack re-validated);
agent and agent_name together is a 400. Persistence: tasks gains
agent_name/agent_version/agent_instructions (migrate + PG schema),
returned by GET /tasks; stored prompt stays clean. Channel: local
and docker dispatch pass --developer-instructions to host.py
dispatch -> create_thread(developerInstructions=...); run/fork do
not re-send. Cloud executor has no such channel, so the worker
falls back to the v1 marked header in the in-memory dispatch
prompt only (DB prompt stays clean). Goal/coordinator tasks
still reject instructions.

Tests: test_agent_def_v2.py (7 tests) + updated v1 suite.
This commit is contained in:
host committed 2026-11-04 18:01:44 +00:00
1 parent 7bb3c37a10
commit 1672ab9ff2
8 files changed
+361 -35

No files matched your search

+13 -4
View File
@@ -1803,7 +1803,8 @@ def _seed_worktree(cwd, seed_dir):
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, seed_dir=None):
depends_on=None, priority=0, max_retries=0, seed_dir=None,
developer_instructions=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):
@@ -1823,8 +1824,10 @@ def dispatch(server, state, name, prompt, repo, model, effort, sandbox,
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)
thread_id = server.create_thread(
cwd, model=model, sandbox=sandbox,
approval_policy=approval_policy,
instructions=developer_instructions)
task = {"name": name, "thread_id": thread_id, "cwd": cwd,
"repo": os.path.abspath(repo) if repo else None,
"branch": branch, "sandbox": sandbox, "status": "running",
@@ -2247,6 +2250,10 @@ def main():
ap.add_argument("--sandbox", default=None)
ap.add_argument("--approval-policy", default=None)
ap.add_argument("--approval", default=None, choices=["auto", "deny"])
ap.add_argument("--developer-instructions", default=None,
help="agent 定义 v2:派发时传给 codex 的"
" developerInstructions(只在 dispatch 建"
" 线程时用,run/goal/fork 不用它)")
ap.add_argument("--turn-timeout", type=int, default=None,
help="单轮等待上限(秒);0 表示不限时、对齐 Codex App "
"等 turn/completed 的语义;不传时用配置 "
@@ -2709,7 +2716,9 @@ def main():
use_worktree=not args.no_worktree,
depends_on=deps, priority=args.priority,
max_retries=args.max_retries,
seed_dir=args.seed_dir)
seed_dir=args.seed_dir,
developer_instructions=(
args.developer_instructions))
# 生产教训:先落盘任务登记再跑 turn,中途被杀/超时后
# 任务还在 host-state 里,可用 run 续跑;否则 turn 中断
# 就等于任务丢了(2026-10-25 委派评审 300 秒超时实测)。