feat: issue-core task intake poll/claim/run (HARNESS-WP-0001-T03)
Add intake client mapping emissions to TaskSpec, CLI poll and run --from-issue-core, keep --task-file for local dev. Completes HARNESS-WP-0001 workplan.
This commit is contained in:
parent
154e535695
commit
66ccd4fa01
7 changed files with 705 additions and 34 deletions
|
|
@ -68,12 +68,123 @@ def _cmd_profiles(_args: argparse.Namespace) -> int:
|
|||
return 0
|
||||
|
||||
|
||||
def _cmd_poll(args: argparse.Namespace) -> int:
|
||||
from agent_harness.intake import IntakeError, IssueCoreClient, poll_next
|
||||
|
||||
try:
|
||||
client = IssueCoreClient()
|
||||
result = poll_next(client, claim=not args.no_claim)
|
||||
except IntakeError as exc:
|
||||
print(f"intake error: {exc}", file=sys.stderr)
|
||||
return 2
|
||||
if result is None:
|
||||
print(json.dumps({"queue": "empty"}, indent=2))
|
||||
return 0
|
||||
issue, spec = result
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"issue_id": issue.issue_id,
|
||||
"state": issue.state,
|
||||
"title": issue.title,
|
||||
"agent": spec.agent,
|
||||
"target_repo": str(spec.target_repo),
|
||||
"completion_event_type": spec.completion_event_type,
|
||||
"labels": spec.labels,
|
||||
"claimed": not args.no_claim,
|
||||
},
|
||||
indent=2,
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
def _cmd_run(args: argparse.Namespace) -> int:
|
||||
from agent_harness.intake import IntakeError, IssueCoreClient, poll_next
|
||||
|
||||
issue_id: str | None = None
|
||||
client: IssueCoreClient | None = None
|
||||
|
||||
if args.from_issue_core:
|
||||
try:
|
||||
client = IssueCoreClient()
|
||||
polled = poll_next(client, claim=True)
|
||||
except IntakeError as exc:
|
||||
print(f"intake error: {exc}", file=sys.stderr)
|
||||
return 2
|
||||
if polled is None:
|
||||
print(json.dumps({"ok": True, "queue": "empty"}, indent=2))
|
||||
return 0
|
||||
issue, spec = polled
|
||||
issue_id = issue.issue_id
|
||||
else:
|
||||
if not args.task_file:
|
||||
print(
|
||||
"error: provide --task-file or --from-issue-core",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 2
|
||||
try:
|
||||
spec = TaskSpec.from_file(args.task_file)
|
||||
except (TaskSpecError, OSError, json.JSONDecodeError) as exc:
|
||||
print(f"invalid task spec: {exc}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
result = run_task(
|
||||
spec,
|
||||
report_to_hub=not args.no_hub,
|
||||
write_metrics=not args.no_metrics,
|
||||
)
|
||||
|
||||
closed = False
|
||||
close_error = ""
|
||||
if client is not None and issue_id:
|
||||
try:
|
||||
if result.ok:
|
||||
client.close(issue_id)
|
||||
closed = True
|
||||
else:
|
||||
client.reopen(issue_id)
|
||||
except IntakeError as exc:
|
||||
close_error = str(exc)
|
||||
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"ok": result.ok,
|
||||
"committed": result.committed,
|
||||
"head_after": result.head_after,
|
||||
"persona_source": result.persona_source,
|
||||
"tool_profile": result.tool_profile,
|
||||
"budget_tokens": result.budget_tokens,
|
||||
"tokens_spent": result.tokens_spent,
|
||||
"execution_time_s": round(result.execution_time_s, 3),
|
||||
"reason": result.reason,
|
||||
"issue_id": issue_id,
|
||||
"issue_closed": closed,
|
||||
"issue_close_error": close_error,
|
||||
},
|
||||
indent=2,
|
||||
)
|
||||
)
|
||||
return 0 if result.ok else 1
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(prog="agent-harness")
|
||||
sub = parser.add_subparsers(dest="command", required=True)
|
||||
|
||||
run = sub.add_parser("run", help="Execute one task from a JSON spec file")
|
||||
run.add_argument("--task-file", required=True)
|
||||
run = sub.add_parser(
|
||||
"run",
|
||||
help="Execute one task from a JSON file or next issue-core emission",
|
||||
)
|
||||
run_src = run.add_mutually_exclusive_group(required=True)
|
||||
run_src.add_argument("--task-file", help="Local JSON task-spec (dev path)")
|
||||
run_src.add_argument(
|
||||
"--from-issue-core",
|
||||
action="store_true",
|
||||
help="Poll issue-core for one open harness-labeled task, claim, run, close",
|
||||
)
|
||||
run.add_argument("--no-hub", action="store_true", help="Skip hub reporting")
|
||||
run.add_argument(
|
||||
"--no-metrics",
|
||||
|
|
@ -127,6 +238,16 @@ def main(argv: list[str] | None = None) -> int:
|
|||
smoke.add_argument("--no-hub", action="store_true", help="Skip hub reporting")
|
||||
smoke.add_argument("--no-push", action="store_true", help="Skip git push after commit")
|
||||
|
||||
poll = sub.add_parser(
|
||||
"poll",
|
||||
help="Peek/claim next open issue-core task labeled for the harness (no execute)",
|
||||
)
|
||||
poll.add_argument(
|
||||
"--no-claim",
|
||||
action="store_true",
|
||||
help="List/map only; do not set in_progress",
|
||||
)
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
if args.command == "validate":
|
||||
|
|
@ -135,6 +256,12 @@ def main(argv: list[str] | None = None) -> int:
|
|||
if args.command == "profiles":
|
||||
return _cmd_profiles(args)
|
||||
|
||||
if args.command == "poll":
|
||||
return _cmd_poll(args)
|
||||
|
||||
if args.command == "run":
|
||||
return _cmd_run(args)
|
||||
|
||||
if args.command == "smoke":
|
||||
from agent_harness.smoke import ensure_sandbox_clone, run_smoke
|
||||
|
||||
|
|
@ -201,34 +328,8 @@ def main(argv: list[str] | None = None) -> int:
|
|||
)
|
||||
return 0 if result.ok else 1
|
||||
|
||||
try:
|
||||
spec = TaskSpec.from_file(args.task_file)
|
||||
except (TaskSpecError, OSError, json.JSONDecodeError) as exc:
|
||||
print(f"invalid task spec: {exc}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
result = run_task(
|
||||
spec,
|
||||
report_to_hub=not args.no_hub,
|
||||
write_metrics=not args.no_metrics,
|
||||
)
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"ok": result.ok,
|
||||
"committed": result.committed,
|
||||
"head_after": result.head_after,
|
||||
"persona_source": result.persona_source,
|
||||
"tool_profile": result.tool_profile,
|
||||
"budget_tokens": result.budget_tokens,
|
||||
"tokens_spent": result.tokens_spent,
|
||||
"execution_time_s": round(result.execution_time_s, 3),
|
||||
"reason": result.reason,
|
||||
},
|
||||
indent=2,
|
||||
)
|
||||
)
|
||||
return 0 if result.ok else 1
|
||||
print(f"unknown command: {args.command}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue