diff --git a/README.md b/README.md index 4fd259f..bfe1760 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ agentrace list # every subagent run: description, duration, siz agentrace check # flag suspicious results agentrace check --severity high # only the ones that definitely matter agentrace check --strict # exit 1 on any high finding (CI-friendly) +agentrace check --slow-seconds 300 # tune the slow-run threshold (default: 900) agentrace show 6e7fAJ8T # read one run in full: prompt, result, findings agentrace stats --json # aggregate, machine-readable ``` diff --git a/agentrace/checks.py b/agentrace/checks.py index 4204050..41e7020 100644 --- a/agentrace/checks.py +++ b/agentrace/checks.py @@ -336,10 +336,13 @@ def check_runaway(run: AgentRun, slow_s: float = 900.0) -> list[Finding]: ] -def analyse(run: AgentRun) -> list[Finding]: +def analyse(run: AgentRun, slow_s: float = 900.0) -> list[Finding]: findings: list[Finding] = [] for check in CHECKS: - findings.extend(check(run)) + if check is check_runaway: + findings.extend(check_runaway(run, slow_s=slow_s)) + else: + findings.extend(check(run)) order = {"high": 0, "medium": 1, "low": 2} findings.sort(key=lambda f: order.get(f.severity, 9)) return findings diff --git a/agentrace/cli.py b/agentrace/cli.py index c511699..c7cda50 100644 --- a/agentrace/cli.py +++ b/agentrace/cli.py @@ -85,7 +85,8 @@ def cmd_check(args) -> int: total_findings = 0 has_high = False for r in runs: - findings = analyse(r) + slow_seconds = getattr(args, "slow_seconds", 900.0) + findings = analyse(r) if slow_seconds == 900.0 else analyse(r, slow_s=slow_seconds) if args.severity: findings = [f for f in findings if f.severity == args.severity] has_high = has_high or any(f.severity == "high" for f in findings) @@ -204,6 +205,12 @@ def main(argv: list[str] | None = None) -> int: c = sub.add_parser("check", help="flag suspicious results") c.add_argument("--severity", choices=["high", "medium", "low"], help="only this severity") + c.add_argument( + "--slow-seconds", + type=float, + default=900.0, + help="flag runs longer than this many seconds (default: 900)", + ) c.add_argument("--strict", action="store_true", help="exit 1 if any displayed high severity finding") c.set_defaults(func=cmd_check) diff --git a/tests/test_agentrace.py b/tests/test_agentrace.py index ec90a7d..22ce91a 100644 --- a/tests/test_agentrace.py +++ b/tests/test_agentrace.py @@ -11,7 +11,7 @@ from __future__ import annotations import json -from datetime import UTC, datetime +from datetime import UTC, datetime, timedelta from agentrace.checks import analyse from agentrace.parse import AgentRun, parse_session @@ -296,6 +296,15 @@ def test_slow_run_is_flagged(): assert "slow_run" in _codes(r) +def test_slow_run_threshold_is_configurable(): + start = datetime(2026, 7, 16, 12, 0, tzinfo=UTC) + thirty_minutes = _run(started_at=start, ended_at=start + timedelta(minutes=30)) + two_minutes = _run(started_at=start, ended_at=start + timedelta(minutes=2)) + + assert "slow_run" not in {f.check for f in analyse(thirty_minutes, slow_s=1801)} + assert "slow_run" in {f.check for f in analyse(two_minutes, slow_s=60)} + + def test_clean_run_produces_nothing(): """The most important test here. A checker that flags everything gets ignored.""" r = _run(