fix(#36): 위임 프롬프트를 argv 가 아닌 파일로 전달하고, 위임 실패를 종료 코드 1 로 보고 #37
No files matched your search
@@ -323,7 +323,11 @@ def _build_parser() -> argparse.ArgumentParser:
|
||||
sub = parser.add_subparsers(dest="command", required=True)
|
||||
|
||||
p_reg = sub.add_parser("register", help="create a pending job; prints the job id")
|
||||
p_reg.add_argument("--prompt", required=True)
|
||||
# Issue #36: a long prompt goes by file - one argv string is capped at
|
||||
# MAX_ARG_STRLEN (131,072 bytes on Linux).
|
||||
reg_prompt = p_reg.add_mutually_exclusive_group(required=True)
|
||||
reg_prompt.add_argument("--prompt")
|
||||
reg_prompt.add_argument("--prompt-file", help="read the prompt from this file (utf-8)")
|
||||
p_reg.add_argument("--agent", default="claude-code")
|
||||
p_reg.add_argument("--agent-session", default="herdr:claude")
|
||||
p_reg.add_argument("--role", default="Worker", help="logical role for the delegated agent (e.g. Worker, Planner, Reviewer)")
|
||||
@@ -365,7 +369,9 @@ def _build_parser() -> argparse.ArgumentParser:
|
||||
p_update.add_argument("--job", required=True)
|
||||
p_update.add_argument("--status", default=None)
|
||||
p_update.add_argument("--agent-session", default=None)
|
||||
p_update.add_argument("--prompt", default=None)
|
||||
upd_prompt = p_update.add_mutually_exclusive_group()
|
||||
upd_prompt.add_argument("--prompt", default=None)
|
||||
upd_prompt.add_argument("--prompt-file", default=None, help="read the prompt from this file (utf-8)")
|
||||
p_update.add_argument("--iteration", type=int, default=None)
|
||||
p_update.add_argument("--attempt-id", type=int, default=None)
|
||||
p_update.add_argument("--role", default=None)
|
||||
@@ -473,15 +479,26 @@ def _operator_write(job_id: str, registry_dir: str, **fields) -> int:
|
||||
return 4
|
||||
|
||||
|
||||
def _prompt_from_args(parser: argparse.ArgumentParser, args: argparse.Namespace) -> Optional[str]:
|
||||
"""The prompt text: from --prompt-file (read verbatim) or from --prompt."""
|
||||
if getattr(args, "prompt_file", None) is None:
|
||||
return args.prompt
|
||||
try:
|
||||
return Path(args.prompt_file).read_text(encoding="utf-8")
|
||||
except (OSError, UnicodeDecodeError) as exc:
|
||||
parser.error(f"cannot read --prompt-file {args.prompt_file}: {exc}")
|
||||
|
||||
|
||||
def main(argv: Optional[List[str]] = None) -> int:
|
||||
mqtt_common.ensure_env_loaded()
|
||||
mqtt_common.setup_logging(logging.INFO)
|
||||
args = _build_parser().parse_args(argv)
|
||||
parser = _build_parser()
|
||||
args = parser.parse_args(argv)
|
||||
rd = args.registry_dir
|
||||
|
||||
if args.command == "register":
|
||||
job_id = register_job(
|
||||
prompt=args.prompt,
|
||||
prompt=_prompt_from_args(parser, args),
|
||||
agent=args.agent,
|
||||
agent_session=args.agent_session,
|
||||
role=args.role,
|
||||
@@ -540,8 +557,9 @@ def main(argv: Optional[List[str]] = None) -> int:
|
||||
fields["status"] = args.status
|
||||
if args.agent_session is not None:
|
||||
fields["agent_session"] = args.agent_session
|
||||
if args.prompt is not None:
|
||||
fields["prompt"] = args.prompt
|
||||
prompt = _prompt_from_args(parser, args)
|
||||
if prompt is not None:
|
||||
fields["prompt"] = prompt
|
||||
if args.iteration is not None:
|
||||
fields["iteration"] = args.iteration
|
||||
if args.attempt_id is not None:
|
||||
|
||||
@@ -44,6 +44,8 @@ multi-agent-mux-delegate-job submit \
|
||||
# Liveness contract (Issue #18): the agent heartbeats every N s (automatic),
|
||||
# the job is unresponsive after K x N s (--idle-timeout overrides), `started`
|
||||
# must arrive within T s of the injection or the inject fails closed.
|
||||
# --prompt-file <path> replaces --prompt for long prompts (Issue #36): one
|
||||
# argv string is capped at 131,072 bytes on Linux. The loop always uses it.
|
||||
|
||||
# 1b) Same, for an agent on another host (no herdr pane, no shared disk):
|
||||
# the brief travels over the broker as a signed retained dispatch and a
|
||||
|
||||
@@ -109,13 +109,15 @@ usage() {
|
||||
cat <<'EOF'
|
||||
multi-agent-mux-delegate-job <command> [options]
|
||||
|
||||
submit --agent <name> --prompt <text> [--workdir <dir>] [--agent-session <label>]
|
||||
submit --agent <name> (--prompt <text> | --prompt-file <path>) [--workdir <dir>] [--agent-session <label>]
|
||||
[--timeout <sec>] [--idle-timeout <sec>] [--validate <script>]
|
||||
[--heartbeat <sec>] [--heartbeat-misses <count>] [--ack-timeout <sec>]
|
||||
[--registry-dir <dir>] [--dry-run] [--role <role_name>]
|
||||
[--type <direct|loop|discuss>] [--reviewer <reviewer_agent>]
|
||||
[--reviewer-session <reviewer_session>] [--max-iterations <count>]
|
||||
[--counterpart-role <role_name>] [--strict-role-check]
|
||||
# --prompt-file passes the prompt by file: one argv string is capped
|
||||
# at 131,072 bytes on Linux (Issue #36). The file is read, not kept.
|
||||
# --agent-session herdr:<name> injects into a local herdr pane;
|
||||
# --agent-session remote:<name> dispatches over the broker (Issue #18).
|
||||
# Liveness contract: the agent heartbeats every --heartbeat N s
|
||||
@@ -131,7 +133,7 @@ EOF
|
||||
}
|
||||
|
||||
# ---- arg parsing helpers --------------------------------------------------
|
||||
AGENT="claude-code"; PROMPT=""; WORKDIR="$(pwd)"; AGENT_SESSION="herdr:claude"
|
||||
AGENT="claude-code"; PROMPT=""; PROMPT_FILE=""; WORKDIR="$(pwd)"; AGENT_SESSION="herdr:claude"
|
||||
TIMEOUT=3600; IDLE_TIMEOUT=""; VALIDATE=""; DRY_RUN=0
|
||||
# Issue #18 liveness contract (N / K / T). An unset --idle-timeout derives K x N.
|
||||
HEARTBEAT_SEC="${MAM_HEARTBEAT_SEC:-30}"; HEARTBEAT_MISSES="${MAM_HEARTBEAT_MISSES:-4}"; ACK_TIMEOUT="${MAM_ACK_TIMEOUT_S:-180}"
|
||||
@@ -148,6 +150,7 @@ parse_opts() {
|
||||
case "$1" in
|
||||
--agent) AGENT="$2"; shift 2;;
|
||||
--prompt) PROMPT="$2"; shift 2;;
|
||||
--prompt-file) PROMPT_FILE="$2"; shift 2;;
|
||||
--workdir) WORKDIR="$2"; shift 2;;
|
||||
--agent-session) AGENT_SESSION="$2"; shift 2;;
|
||||
--timeout) TIMEOUT="$2"; shift 2;;
|
||||
@@ -183,7 +186,17 @@ _to_lower() {
|
||||
|
||||
cmd_submit() {
|
||||
parse_opts "$@"
|
||||
[[ -n "$PROMPT" ]] || { echo "submit requires --prompt" >&2; exit 1; }
|
||||
if [[ -n "$PROMPT_FILE" ]]; then
|
||||
[[ -z "$PROMPT" ]] || { echo "submit takes --prompt or --prompt-file, not both" >&2; exit 1; }
|
||||
[[ "$PROMPT_FILE" == /* ]] || PROMPT_FILE="$(pwd)/$PROMPT_FILE"
|
||||
[[ -f "$PROMPT_FILE" && -r "$PROMPT_FILE" ]] || { echo "submit: cannot read --prompt-file $PROMPT_FILE" >&2; exit 1; }
|
||||
PROMPT="$(<"$PROMPT_FILE")"
|
||||
fi
|
||||
[[ -n "$PROMPT" || -n "$PROMPT_FILE" ]] || { echo "submit requires --prompt or --prompt-file" >&2; exit 1; }
|
||||
# Issue #36: the prompt reaches the registry by file when it came by file,
|
||||
# never as one argv string (capped at 131,072 bytes).
|
||||
local reg_prompt=(--prompt "$PROMPT")
|
||||
[[ -n "$PROMPT_FILE" ]] && reg_prompt=(--prompt-file "$PROMPT_FILE")
|
||||
PY="$(pick_python)"
|
||||
[[ "$PY" == */* && "$PY" != /* ]] && PY="$(pwd)/$PY"
|
||||
cd "$WORKDIR"
|
||||
@@ -192,7 +205,7 @@ cmd_submit() {
|
||||
|
||||
# 1) register job (prints the new job id)
|
||||
JOB_ID="$("$PY" "$SCRIPT_DIR/scripts/registry.py" --registry-dir "$REGISTRY_DIR" register \
|
||||
--prompt "$PROMPT" --agent "$AGENT" --agent-session "$AGENT_SESSION" --role "$DELEGATE_ROLE" \
|
||||
"${reg_prompt[@]}" --agent "$AGENT" --agent-session "$AGENT_SESSION" --role "$DELEGATE_ROLE" \
|
||||
--timeout "$TIMEOUT" --idle-timeout "$IDLE_TIMEOUT" \
|
||||
--heartbeat-sec "$HEARTBEAT_SEC" --heartbeat-misses "$HEARTBEAT_MISSES" --ack-timeout "$ACK_TIMEOUT" \
|
||||
--transport "$(_is_remote "$AGENT_SESSION" && echo remote || echo herdr)" \
|
||||
@@ -392,6 +405,8 @@ EOF
|
||||
## 🔎 Task Description
|
||||
$current_prompt
|
||||
EOF
|
||||
# Issue #36: the registry gets the prompt by file, not as one argv string
|
||||
printf '%s' "$current_prompt" > "$job_dir/prompt.md"
|
||||
echo "provisioned iteration brief: $job_dir/brief.md"
|
||||
|
||||
attempt=$((attempt + 1))
|
||||
@@ -432,7 +447,7 @@ EOF
|
||||
"$PY" "$SCRIPT_DIR/scripts/registry.py" --registry-dir "$REGISTRY_DIR" update \
|
||||
--job "$JOB_ID" \
|
||||
--agent-session "$current_session" \
|
||||
--prompt "$current_prompt" \
|
||||
--prompt-file "$job_dir/prompt.md" \
|
||||
--iteration "$iteration" \
|
||||
--attempt-id "$attempt" \
|
||||
--role "$display_role" \
|
||||
|
||||
Reference in new issue
Block a user