"""Task/subagent display state for messaging transcripts.""" from typing import Any from loguru import logger from .segments import SubagentSegment class SubagentState: """Track active Task tool calls that suppress nested text/thinking output.""" def __init__(self, *, debug: bool = False) -> None: self._stack: list[str] = [] self._segments: list[SubagentSegment] = [] self._debug = debug @property def open_ids(self) -> tuple[str, ...]: return tuple(self._stack) def in_subagent(self) -> bool: return bool(self._stack) def current_segment(self) -> SubagentSegment | None: return self._segments[-1] if self._segments else None def push(self, tool_id: str, segment: SubagentSegment) -> None: marker = str(tool_id or "").strip() or f"__task_{len(self._stack) + 1}" self._stack.append(marker) self._segments.append(segment) if self._debug: logger.debug( "SUBAGENT_STACK: push id=%r depth=%d heading=%r", marker, len(self._stack), segment.description, ) def close_for_tool_result(self, tool_id: str, *, tool_name: str | None) -> bool: tool_id = str(tool_id or "").strip() popped = self._pop(tool_id) top = self._stack[-1] if self._stack else "" looks_like_task_id = "task" in tool_id.lower() if ( not popped and tool_id and top.startswith("__task_") and tool_name in (None, "Task") and looks_like_task_id ): return self._pop("") return popped def _pop(self, tool_id: str) -> bool: tool_id = str(tool_id or "").strip() if not self._stack: return False if tool_id: if _ids_roughly_match(self._stack[-1], tool_id): self._pop_to_depth(len(self._stack) - 1, tool_id, "LIFO") return True for idx in range(len(self._stack) - 1, -1, -1): if _ids_roughly_match(self._stack[idx], tool_id): self._pop_to_depth(idx, tool_id, "matched") return True return False if self._stack[-1].startswith("__task_"): self._pop_to_depth(len(self._stack) - 1, self._stack[-1], "synthetic") return True return False def _pop_to_depth(self, idx: int, requested_id: str, reason: str) -> None: while len(self._stack) > idx: popped = self._stack.pop() if self._segments: self._segments.pop() if self._debug: logger.debug( "SUBAGENT_STACK: pop id=%r depth=%d (%s=%r)", popped, len(self._stack), reason, requested_id, ) def task_heading_from_input(input_value: Any) -> str: if isinstance(input_value, dict): for key in ("description", "subagent_type", "type"): value = str(input_value.get(key, "") or "").strip() if value: return value return "Subagent" def _ids_roughly_match(stack_id: str, result_id: str) -> bool: if not stack_id or not result_id: return False return ( stack_id == result_id or stack_id.startswith(result_id) or result_id.startswith(stack_id) )