fix(prax): cap monitor agent events per batch to prevent doom-scrolling
This commit is contained in:
@@ -1 +0,0 @@
|
||||
{"file": "/home/patrick/Projects/AIPass/src/aipass/drone/apps/modules/git_module.py", "errors": [{"line": 671, "message": "E501: Line too long (193 > 120)"}]}
|
||||
@@ -535,11 +535,15 @@ class MonitoringFileHandler(FileSystemEventHandler):
|
||||
# CLAUDE CODE AGENT ACTIVITY PARSING (JSONL sessions)
|
||||
# =========================================================================
|
||||
|
||||
_MAX_AGENT_EVENTS_PER_BATCH = 3
|
||||
|
||||
def _parse_agent_activity(self, file_path, branch):
|
||||
"""Parse Claude Code session JSONL to show agent actions.
|
||||
|
||||
Returns True if an event was emitted (or deduped), False on failure.
|
||||
Iterates forward through new lines, emitting all distinct actions.
|
||||
Iterates forward through all new lines (for model extraction), but only
|
||||
emits the last few distinct actions to prevent doom-scrolling when large
|
||||
batches accumulate between polls or after compaction resets.
|
||||
"""
|
||||
try:
|
||||
path_key = str(file_path)
|
||||
@@ -560,6 +564,9 @@ class MonitoringFileHandler(FileSystemEventHandler):
|
||||
if not lines:
|
||||
return True
|
||||
|
||||
pending_actions = []
|
||||
last_seen = self._last_agent_action.get(path_key)
|
||||
|
||||
for line in lines:
|
||||
try:
|
||||
entry = _json.loads(line)
|
||||
@@ -575,10 +582,12 @@ class MonitoringFileHandler(FileSystemEventHandler):
|
||||
if not action_text:
|
||||
continue
|
||||
|
||||
if self._last_agent_action.get(path_key) == action_text:
|
||||
if action_text == last_seen:
|
||||
continue
|
||||
self._last_agent_action[path_key] = action_text
|
||||
last_seen = action_text
|
||||
pending_actions.append(action_text)
|
||||
|
||||
for action_text in pending_actions[-self._MAX_AGENT_EVENTS_PER_BATCH :]:
|
||||
tagged_branch = self._tag_branch_with_model(path_key, branch)
|
||||
evt = MonitoringEvent(
|
||||
priority=1,
|
||||
@@ -591,6 +600,9 @@ class MonitoringFileHandler(FileSystemEventHandler):
|
||||
if self._event_queue:
|
||||
self._event_queue.enqueue(evt)
|
||||
|
||||
if pending_actions:
|
||||
self._last_agent_action[path_key] = pending_actions[-1]
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -1763,6 +1763,56 @@ class TestParseAgentActivity:
|
||||
result = handler._parse_agent_activity(mock_path, "PRAX")
|
||||
assert result is True
|
||||
|
||||
def test_batch_cap_limits_emitted_events(self):
|
||||
"""Should only emit last N events when batch exceeds cap."""
|
||||
mod, _, _, _, queue = _import_filesystem_handler()
|
||||
handler = _make_handler(mod, queue=queue)
|
||||
mock_path = MagicMock()
|
||||
mock_path.__str__ = MagicMock(return_value="/fake/claude.jsonl")
|
||||
mock_path.name = "claude.jsonl"
|
||||
entries = []
|
||||
tools = ["Read", "Edit", "Write", "Grep", "Glob", "Bash"]
|
||||
for i, tool in enumerate(tools):
|
||||
entries.append(
|
||||
{
|
||||
"type": "assistant",
|
||||
"message": {"content": [{"type": "tool_use", "name": tool, "input": {"file_path": f"f{i}.py"}}]},
|
||||
}
|
||||
)
|
||||
data = "\n".join(json.dumps(e) for e in entries) + "\n"
|
||||
mock_path.stat.return_value.st_size = len(data)
|
||||
with patch("builtins.open", _mopen(read_data=data)):
|
||||
result = handler._parse_agent_activity(mock_path, "PRAX")
|
||||
assert result is True
|
||||
cap = mod.MonitoringFileHandler._MAX_AGENT_EVENTS_PER_BATCH
|
||||
assert queue.enqueue.call_count == cap
|
||||
|
||||
def test_batch_cap_emits_tail_not_head(self):
|
||||
"""Should emit the last actions in a large batch, not the first."""
|
||||
mod, mock_eq, _, _, queue = _import_filesystem_handler()
|
||||
handler = _make_handler(mod, queue=queue)
|
||||
mock_path = MagicMock()
|
||||
mock_path.__str__ = MagicMock(return_value="/fake/claude.jsonl")
|
||||
mock_path.name = "claude.jsonl"
|
||||
entries = []
|
||||
for i in range(6):
|
||||
entries.append(
|
||||
{
|
||||
"type": "assistant",
|
||||
"message": {"content": [{"type": "tool_use", "name": f"Tool{i}", "input": {}}]},
|
||||
}
|
||||
)
|
||||
data = "\n".join(json.dumps(e) for e in entries) + "\n"
|
||||
mock_path.stat.return_value.st_size = len(data)
|
||||
with patch("builtins.open", _mopen(read_data=data)):
|
||||
handler._parse_agent_activity(mock_path, "PRAX")
|
||||
cap = mod.MonitoringFileHandler._MAX_AGENT_EVENTS_PER_BATCH
|
||||
assert queue.enqueue.call_count == cap
|
||||
last_call_kwargs = mock_eq.MonitoringEvent.call_args_list[-1][1]
|
||||
assert "Tool5" in last_call_kwargs["message"]
|
||||
first_call_kwargs = mock_eq.MonitoringEvent.call_args_list[-cap][1]
|
||||
assert "Tool3" in first_call_kwargs["message"]
|
||||
|
||||
|
||||
# =============================================
|
||||
# CHECK COMMAND INDICATOR TESTS
|
||||
|
||||
Reference in New Issue
Block a user