Source code for ralph.agents.execution_state.claude_interactive_execution_strategy
"""Execution strategy for interactive Claude agents."""
from __future__ import annotations
from typing import TYPE_CHECKING
from ralph.agents.activity import AgentActivityKind, AgentActivitySignal
from ralph.agents.parsers.claude_interactive import ClaudeInteractiveTranscriptParser
from ._helpers import _check_signals_terminal
from .agent_execution_state import AgentExecutionState
from .claude_execution_strategy import ClaudeExecutionStrategy
if TYPE_CHECKING:
from ralph.agents.completion_signals import CompletionSignals
from ralph.process.liveness import LivenessProbe
from ._live_descendant_handle import _LiveDescendantHandle
[docs]
class ClaudeInteractiveExecutionStrategy(ClaudeExecutionStrategy):
"""Interactive Claude session strategy.
Uses a VT-aware transcript parser before falling back to the headless Claude
classifier so TUI repaint noise does not downgrade meaningful tool/lifecycle
lines into generic output.
"""
def __init__(self) -> None:
self._transcript_parser = ClaudeInteractiveTranscriptParser()
[docs]
def classify_activity_line(self, line: str) -> AgentActivitySignal | None:
events = self._transcript_parser.feed(line)
if events:
tool_result_event = None
lifecycle_event = None
output_event = None
for event in events:
if event.kind == "tool_use":
return AgentActivitySignal(AgentActivityKind.TOOL_USE, raw=event.text)
if event.kind in {"lifecycle", "session"} and lifecycle_event is None:
lifecycle_event = event
continue
if event.kind == "tool_result" and tool_result_event is None:
tool_result_event = event
continue
if output_event is None:
output_event = event
if lifecycle_event is not None:
return AgentActivitySignal(AgentActivityKind.LIFECYCLE, raw=lifecycle_event.text)
if tool_result_event is not None:
return AgentActivitySignal(
AgentActivityKind.OUTPUT_LINE,
raw=tool_result_event.text,
)
if output_event is not None:
return AgentActivitySignal(AgentActivityKind.OUTPUT_LINE, raw=output_event.text)
return super().classify_activity_line(line)
def supports_session_continuation(self) -> bool:
return True
def classify_exit(
self,
handle: _LiveDescendantHandle,
completion_signals: CompletionSignals,
liveness_probe: LivenessProbe | None = None,
) -> AgentExecutionState:
del handle, liveness_probe
if _check_signals_terminal(completion_signals):
return AgentExecutionState.TERMINAL_COMPLETE
return AgentExecutionState.RESUMABLE_CONTINUE