Compare commits

...

1 Commits

Author SHA1 Message Date
Alexander Whitestone
6466484953 feat: marathon session limits — cap, checkpoint, rotate (closes #326)
Some checks failed
Docker Build and Publish / build-and-push (pull_request) Has been skipped
Nix / nix (ubuntu-latest) (pull_request) Failing after 3s
Supply Chain Audit / Scan PR for supply chain risks (pull_request) Successful in 48s
Docs Site Checks / docs-site-checks (pull_request) Failing after 2m52s
Tests / e2e (pull_request) Successful in 2m46s
Tests / test (pull_request) Failing after 33m39s
Nix / nix (macos-latest) (pull_request) Has been cancelled
170 sessions exceed 100 msgs. Longest: 1,643 (~40h). 45-84% error rate.

Config (agent.marathon in config.yaml):
- warn_at: 200 (soft cap — inject system nudge once)
- cap_at: 300 (hard cap — stop conversation)

Soft cap: agent sees nudge suggesting fresh session.
Hard cap: turn_exit_reason=marathon_cap_reached, loop breaks.
Zero overhead below threshold.
1 file, 29 insertions.
2026-04-13 22:07:33 -04:00

View File

@@ -1247,6 +1247,14 @@ class AIAgent:
_agent_section = {}
self._tool_use_enforcement = _agent_section.get("tool_use_enforcement", "auto")
# Marathon session limits (issue #326)
_marathon_cfg = _agent_section.get("marathon", {})
if not isinstance(_marathon_cfg, dict):
_marathon_cfg = {}
self._marathon_warn = int(_marathon_cfg.get("warn_at", 200))
self._marathon_cap = int(_marathon_cfg.get("cap_at", 300))
self._marathon_nudge_sent = False
# Initialize context compressor for automatic context management
# Compresses conversation when approaching model's context limit
# Configuration via config.yaml (compression section)
@@ -8033,7 +8041,26 @@ class AIAgent:
if not self.quiet_mode:
self._safe_print("\n⚡ Breaking out of tool loop due to interrupt...")
break
# Marathon session limits (#326)
_msg_count = len(messages)
if self._marathon_cap > 0 and _msg_count >= self._marathon_cap:
_turn_exit_reason = "marathon_cap_reached"
if not self.quiet_mode:
self._safe_print(f"\n🛑 Cap: {_msg_count}/{self._marathon_cap}. Start fresh.")
messages.append({"role": "system", "content": (
f"[SYSTEM: Session at {_msg_count} msgs. Error rate 45-84% at this length. End session.]"
)})
break
elif (self._marathon_warn > 0 and _msg_count >= self._marathon_warn
and not self._marathon_nudge_sent):
self._marathon_nudge_sent = True
messages.append({"role": "system", "content": (
f"[SYSTEM: {_msg_count} msgs reached. Wrap up and start fresh.]"
)})
if not self.quiet_mode:
self._safe_print(f"\n⚠️ {_msg_count} msgs. Consider fresh session.")
api_call_count += 1
self._api_call_count = api_call_count
self._touch_activity(f"starting API call #{api_call_count}")
@@ -10512,6 +10539,7 @@ class AIAgent:
"completed": completed,
"partial": False, # True only when stopped due to invalid tool calls
"interrupted": interrupted,
"turn_exit_reason": _turn_exit_reason,
"response_previewed": getattr(self, "_response_was_previewed", False),
"model": self.model,
"provider": self.provider,