Files
Agent-System/scripts/herdr-agent-dispatch.sh

129 lines
4.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Dispatch to a standard live Herdr agent, with conservative failover.
set -euo pipefail
usage() {
cat <<'EOF'
用法:
scripts/herdr-agent-dispatch.sh --explain <lane>
scripts/herdr-agent-dispatch.sh [--timeout-ms N] <lane> <prompt>
lane: governance | critical-review | implementation | ui | test | fast-check | long-context
EOF
}
route_for() {
case "$1" in
governance) printf '%s\n' orchestrator_codex context_kimi implementer_cursor reserve_claude ;;
critical-review) printf '%s\n' orchestrator_codex reserve_claude context_kimi ;;
implementation) printf '%s\n' implementer_cursor implementer_cline verifier_antigravity orchestrator_codex ;;
ui) printf '%s\n' implementer_cursor verifier_antigravity implementer_cline ;;
test) printf '%s\n' implementer_cursor implementer_cline verifier_antigravity ;;
fast-check) printf '%s\n' verifier_antigravity implementer_cline implementer_cursor ;;
long-context) printf '%s\n' context_pi context_kimi orchestrator_codex ;;
*) return 1 ;;
esac
}
if [[ "${1:-}" == "--explain" ]]; then
[[ $# -eq 2 ]] || { usage >&2; exit 2; }
if ! route_for "$2"; then
printf '[错误] 未知 lane: %s\n' "$2" >&2
exit 2
fi
exit 0
fi
timeout_ms=120000
if [[ "${1:-}" == "--timeout-ms" ]]; then
[[ $# -ge 4 ]] || { usage >&2; exit 2; }
timeout_ms="$2"
shift 2
fi
if [[ $# -ne 2 ]]; then
usage >&2
exit 2
fi
lane="$1"
prompt="$2"
if ! routes="$(route_for "${lane}")"; then
printf '[错误] 未知 lane: %s\n' "${lane}" >&2
exit 2
fi
if [[ ! "${timeout_ms}" =~ ^[1-9][0-9]*$ ]]; then
printf '[错误] timeout 必须是正整数毫秒。\n' >&2
exit 2
fi
if [[ "${HERDR_ENV:-}" != "1" ]]; then
printf '[错误] 当前进程不在 Herdr 管理的 pane 中。\n' >&2
exit 1
fi
command -v jq >/dev/null 2>&1 || { printf '[错误] 缺少 jq。\n' >&2; exit 1; }
repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
if [[ -z "${repo_root}" || ! -f "${repo_root}/AGENTS.md" || ! -f "${repo_root}/PROJECT.md" ]]; then
printf '[错误] 必须从 Agent-System 任务 worktree 派发。\n' >&2
exit 1
fi
branch="$(git branch --show-current)"
if [[ "${branch}" != feature/* && "${branch}" != fix/* ]]; then
printf '[错误] 只允许从 feature/* 或 fix/* 分支派发,当前为 %s。\n' "${branch}" >&2
exit 1
fi
current_cwd="$(realpath -m "${repo_root}")"
agent_json="$(herdr agent list)"
dispatch_id="${BASHPID:-$$}-${SECONDS}"
retry_prefix="HERDR_RETRY:${dispatch_id}"
protocol="$(printf '\n\n[Herdr failover protocol]\n只有在尚未写文件、未运行有副作用命令,且仅因额度、认证或客户端故障无法开始时,回复独占一行 %s:quota、%s:auth 或 %s:client。已经开始工作时不得返回该标记;请写 handoff 并说明当前状态。' "${retry_prefix}" "${retry_prefix}" "${retry_prefix}")"
while IFS= read -r target; do
row="$(jq -c --arg name "${target}" '.result.agents[] | select(.name == $name)' <<<"${agent_json}" | head -n 1)"
if [[ -z "${row}" ]]; then
printf '[跳过] %s 未在线。\n' "${target}" >&2
continue
fi
status="$(jq -r '.agent_status' <<<"${row}")"
agent_cwd="$(jq -r '.cwd' <<<"${row}")"
if [[ "${agent_cwd}" != "${current_cwd}" ]]; then
printf '[跳过] %s cwd=%s,不是当前任务 worktree。\n' "${target}" "${agent_cwd}" >&2
continue
fi
if [[ "${status}" != "idle" && "${status}" != "done" ]]; then
printf '[跳过] %s 状态为 %s。\n' "${target}" "${status}" >&2
continue
fi
printf '[派发] lane=%s target=%s\n' "${lane}" "${target}" >&2
set +e
result="$(herdr agent prompt "${target}" "${prompt}${protocol}" --wait --timeout "${timeout_ms}" 2>&1)"
rc=$?
set -e
if [[ ${rc} -ne 0 ]]; then
if rg -q 'agent_blocked|agent_not_found|agent_not_ready' <<<"${result}"; then
printf '[降级] %s 在投递前不可用。\n' "${target}" >&2
continue
fi
printf '%s\n' "${result}" >&2
printf '[停止] 结果可能已投递;为避免重复执行,不自动切换备援。\n' >&2
exit 75
fi
transcript="$(herdr agent read "${target}" --source recent-unwrapped --lines 120 2>/dev/null || true)"
if rg -q "^${retry_prefix}:(quota|auth|client)$" <<<"${transcript}"; then
printf '[降级] %s 明确报告写入前失败。\n' "${target}" >&2
agent_json="$(herdr agent list)"
continue
fi
printf '%s\n' "${result}"
exit 0
done <<<"${routes}"
printf '[错误] lane=%s 无可用实例;请用 herdr-agent-start.sh 启动标准实例。\n' "${lane}" >&2
exit 69