"""AdvancePhaseTool — LLM-driven phase transition (G6, KTD6). Registered alongside other tools when ReActEngine has a phase_policy set. The LLM calls this tool to signal "I'm done planning, move to building". """ from __future__ import annotations import logging from typing import TYPE_CHECKING from agentkit.tools.base import Tool if TYPE_CHECKING: from agentkit.core.react import ReActEngine logger = logging.getLogger(__name__) class AdvancePhaseTool(Tool): """Tool that advances the ReActEngine's current phase. KTD6: LLM-driven phase transitions. Auto-advance is opt-in via ``plan_exec.auto_advance_after_steps``; this tool is the manual path. The tool holds a weak reference to the engine (via bound method ``engine.advance_phase``) — registered only when phase_policy is set. """ def __init__( self, engine: "ReActEngine", name: str = "advance_phase", description: str | None = None, version: str = "1.0.0", tags: list[str] | None = None, ): super().__init__( name=name, description=description or ( "Advance the PLAN_EXEC phase state machine to the next phase " "(Planning → Building → Verification → Delivery). Call this " "when you have finished the current phase's work and are ready " "to move on. Returns the new phase name or an error if you " "are already at the final (Delivery) phase." ), input_schema={ "type": "object", "properties": {}, "additionalProperties": False, }, version=version, tags=tags or ["phase", "control"], ) self._engine = engine async def execute(self, **kwargs) -> dict[str, object]: # Capture previous phase before transition (engine is single-threaded per request). previous = self._engine.current_phase new_phase = self._engine.advance_phase() if new_phase is None: # Either no policy set, or already at DELIVERY. current = self._engine.current_phase if current is None: return { "is_error": True, "error": "no_phase_policy", "message": "No phase policy is set — advance_phase is a no-op.", } return { "is_error": True, "error": "already_at_final_phase", "message": (f"Already at final phase ({current.value}). Cannot advance further."), "current_phase": current.value, } return { "is_error": False, "previous_phase": previous.value if previous else "", "current_phase": new_phase.value, "message": f"Phase advanced to {new_phase.value}.", }