Skip to content

Commit 05bdb4b

Browse files
committed
Add agent run wrapper
1 parent 49e8ec5 commit 05bdb4b

1 file changed

Lines changed: 103 additions & 0 deletions

File tree

agentflow/runner.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
from __future__ import annotations
2+
3+
import subprocess
4+
from datetime import datetime
5+
from pathlib import Path
6+
7+
from .context_builder import build_context_pack
8+
from .git_tools import git_diff, run_cmd
9+
from .models import RunRecord, utc_now
10+
from .review import render_review_report
11+
from .storage import Store
12+
13+
14+
def make_run_id(task_id: str) -> str:
15+
return task_id + "-RUN-" + datetime.now().strftime("%H%M%S")
16+
17+
18+
def run_task(store: Store, task_id: str, agent: str = "manual", command: str = "",
19+
checks: list[str] | None = None, timeout: int | None = None) -> RunRecord:
20+
store.require()
21+
task = store.get_task(task_id)
22+
context_dir = build_context_pack(store, task_id)
23+
context = (context_dir / "context-pack.md").read_text(encoding="utf-8")
24+
25+
run_id = make_run_id(task.id)
26+
run_dir = store.base / "runs" / run_id
27+
run_dir.mkdir(parents=True, exist_ok=True)
28+
29+
prompt_path = run_dir / "prompt.md"
30+
stdout_path = run_dir / "stdout.txt"
31+
stderr_path = run_dir / "stderr.txt"
32+
diff_path = run_dir / "diff.patch"
33+
report_path = run_dir / "review-report.md"
34+
35+
prompt_path.write_text(context, encoding="utf-8")
36+
37+
run = RunRecord(
38+
id=run_id,
39+
task_id=task.id,
40+
agent=agent,
41+
command=command,
42+
status="running",
43+
started_at=utc_now(),
44+
prompt_path=str(prompt_path.relative_to(store.root)),
45+
stdout_path=str(stdout_path.relative_to(store.root)),
46+
stderr_path=str(stderr_path.relative_to(store.root)),
47+
diff_path=str(diff_path.relative_to(store.root)),
48+
report_path=str(report_path.relative_to(store.root)),
49+
)
50+
store.add_run(run)
51+
52+
stdout = ""
53+
stderr = ""
54+
returncode = 0
55+
if command:
56+
# The context pack path is passed via env-like shell variable for simple wrappers.
57+
shell_command = command.replace("{prompt}", str(prompt_path))
58+
try:
59+
result = run_cmd(shell_command, store.root, timeout=timeout)
60+
stdout = result.stdout
61+
stderr = result.stderr
62+
returncode = result.returncode
63+
except subprocess.TimeoutExpired as exc:
64+
stdout = exc.stdout or ""
65+
stderr = (exc.stderr or "") + f"\n<timeout after {timeout}s>"
66+
returncode = 124
67+
68+
stdout_path.write_text(stdout, encoding="utf-8")
69+
stderr_path.write_text(stderr, encoding="utf-8")
70+
71+
check_results: list[dict] = []
72+
for check in checks or []:
73+
try:
74+
result = run_cmd(check, store.root, timeout=timeout)
75+
check_results.append({
76+
"command": check,
77+
"returncode": result.returncode,
78+
"stdout": result.stdout[-4000:],
79+
"stderr": result.stderr[-4000:],
80+
})
81+
except subprocess.TimeoutExpired as exc:
82+
check_results.append({
83+
"command": check,
84+
"returncode": 124,
85+
"stdout": exc.stdout or "",
86+
"stderr": (exc.stderr or "") + f"\n<timeout after {timeout}s>",
87+
})
88+
89+
diff_text = git_diff(store.root)
90+
diff_path.write_text(diff_text, encoding="utf-8")
91+
92+
failed_checks = [c for c in check_results if c.get("returncode") != 0]
93+
if returncode != 0 or failed_checks:
94+
run.status = "failed"
95+
else:
96+
run.status = "finished"
97+
run.finished_at = utc_now()
98+
run.checks = check_results
99+
store.update_run(run)
100+
101+
report = render_review_report(task, run, diff_text, stdout, stderr)
102+
report_path.write_text(report, encoding="utf-8")
103+
return run

0 commit comments

Comments
 (0)