fix: resolve all CI failures — format, lint, mypy, and review comments

- Format: auto-reformat agent_tui.py, benchmark.py, coworker_tools.py via ruff
- Lint: 0 remaining errors after format pass
- Mypy: fix _NullPrinter to subclass Printer for type compatibility in
  executor.py, planning.py, and skill_builder.py; add isinstance(r, Message)
  guards in spawn_tools.py; annotate return types and fix dict type params
  and MCPToolResolver logger type in new_agent.py; add missing printer args
  to get_llm_response calls
- cli.py: fix _read_config to use sentinel so falsy values (0, false) are
  returned correctly instead of being treated as missing keys
- create_agent.py: replace regex-based JSONC comment stripper with a
  token-aware parser that preserves // inside quoted strings (e.g. URLs)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
alex-clawd
2026-05-13 02:06:51 -07:00
parent d80511898c
commit 48a861aa1a
8 changed files with 63 additions and 22 deletions

View File

@@ -533,8 +533,13 @@ class AgentTUI(App[None]):
yield Static("AGENTS", classes="sidebar-label")
yield OptionList(id="agent-list")
with TabPane("Memory", id="tab-memory"):
yield Static("Click below to open the memory browser.", id="memory-scope-label")
yield Button("Open Memory Browser", id="btn-memory", variant="default")
yield Static(
"Click below to open the memory browser.",
id="memory-scope-label",
)
yield Button(
"Open Memory Browser", id="btn-memory", variant="default"
)
with Vertical(id="chat-area"):
yield VerticalScroll(id="chat-scroll")
with Horizontal(id="input-row"):
@@ -1617,6 +1622,7 @@ class AgentTUI(App[None]):
callback=self._on_room_created,
)
return
# ── Room creation ──
def _on_room_created(self, result: dict[str, Any] | None) -> None:

View File

@@ -111,7 +111,6 @@ def load_benchmark_cases(path: str | Path) -> LoadedCases:
return LoadedCases(cases, threshold)
def _check_expected(expected: str, actual: str) -> tuple[bool, float]:
"""Check if expected output is found in actual (case-insensitive substring match).

View File

@@ -604,10 +604,12 @@ def _read_config(*keys: str) -> Any:
"""Read a nested value from config.json (JSONC-safe).
Example: _read_config("test", "threshold") reads config["test"]["threshold"].
Returns None only when the key is missing, not when the value is falsy.
"""
import json
from pathlib import Path
_MISSING = object()
config_path = Path("config.json")
if not config_path.exists():
return None
@@ -618,8 +620,8 @@ def _read_config(*keys: str) -> Any:
for k in keys:
if not isinstance(data, dict):
return None
data = data.get(k)
if data is None:
data = data.get(k, _MISSING)
if data is _MISSING:
return None
return data
except Exception:

View File

@@ -812,10 +812,30 @@ def _prompt_agent_name() -> str:
)
_JSONC_TOKEN_RE = re.compile(
r'"(?:[^"\\]|\\.)*"' # double-quoted string
r"|'(?:[^'\\]|\\.)*'" # single-quoted string (not standard JSON, but safe)
r"|/\*.*?\*/" # /* block comment */
r"|//[^\n]*" # // line comment
r"|.", # any other character
re.DOTALL,
)
def _strip_jsonc(text: str) -> str:
"""Strip // and /* */ comments from JSONC text, then fix trailing commas."""
result = re.sub(r"(?<!:)//.*?$", "", text, flags=re.MULTILINE)
result = re.sub(r"/\*.*?\*/", "", result, flags=re.DOTALL)
"""Strip // and /* */ comments from JSONC text, then fix trailing commas.
Only strips comments that appear outside of quoted strings, so double
slashes inside string values (e.g. URLs) are preserved correctly.
"""
def _replacer(match: re.Match[str]) -> str:
token = match.group(0)
if token.startswith(("//", "/*")):
return ""
return token
result = _JSONC_TOKEN_RE.sub(_replacer, text)
result = re.sub(r",\s*([}\]])", r"\1", result)
return result

View File

@@ -244,7 +244,9 @@ class DelegateToCoworkerTool(BaseTool):
)
raise
async def _arun(self, message: str, fire_and_forget: bool = False, **kwargs: Any) -> str:
async def _arun(
self, message: str, fire_and_forget: bool = False, **kwargs: Any
) -> str:
"""Async delegation — avoids blocking the event loop."""
from crewai.new_agent.events import (
NewAgentDelegationCompletedEvent,

View File

@@ -11,6 +11,7 @@ import re
import time
from typing import TYPE_CHECKING, Any
from crewai_core.printer import Printer
from pydantic import BaseModel, Field, PrivateAttr
from crewai.new_agent.models import (
@@ -2419,8 +2420,8 @@ class ConversationalAgentExecutor(BaseModel):
pass
class _NullPrinter:
"""Minimal printer that swallows output."""
class _NullPrinter(Printer):
"""Minimal printer that subclasses Printer but swallows all output."""
def print(self, *args: Any, **kwargs: Any) -> None:
def print(self, *args: Any, **kwargs: Any) -> None: # type: ignore[override]
pass

View File

@@ -36,11 +36,11 @@ def _get_init_chain() -> set[str]:
"""Return the thread-local set of agent IDs currently being initialized."""
if not hasattr(_init_chain, "agent_ids"):
_init_chain.agent_ids = set()
return _init_chain.agent_ids
return _init_chain.agent_ids # type: ignore[no-any-return]
# ── GAP-63: Process-level AMP definition cache ──────────────────
_amp_cache: dict[str, dict] = {}
_amp_cache: dict[str, dict[str, Any]] = {}
def clear_amp_cache() -> None:
@@ -286,8 +286,10 @@ class NewAgent(BaseModel):
if self.mcps:
try:
from crewai.mcp.tool_resolver import MCPToolResolver
from crewai.utilities.logger import Logger as _CrewLogger
resolver = MCPToolResolver(agent=self, logger=self._logger)
_mcp_logger = _CrewLogger(verbose=self.verbose)
resolver = MCPToolResolver(agent=self, logger=_mcp_logger)
mcp_tools = resolver.resolve(self.mcps)
resolved.extend(mcp_tools)
except Exception as e:
@@ -631,7 +633,7 @@ class NewAgent(BaseModel):
if self.on_message:
self.on_message(user_msg)
response = executor.invoke(user_msg)
response: Message = executor.invoke(user_msg)
if self.on_complete:
self.on_complete(response)
@@ -656,7 +658,7 @@ class NewAgent(BaseModel):
if self.on_message:
self.on_message(user_msg)
response = await executor.ainvoke(user_msg)
response: Message = await executor.ainvoke(user_msg)
if self.on_complete:
self.on_complete(response)
@@ -793,10 +795,13 @@ class NewAgent(BaseModel):
messages: list[LLMMessage] = [
format_message_for_llm(prompt, role="user")
]
from crewai.new_agent.executor import _NullPrinter
reasoning_text = get_llm_response(
llm=self._llm_instance,
messages=messages,
callbacks=[],
printer=_NullPrinter(),
)
if reasoning_text:
reasoning_str = str(reasoning_text).strip()
@@ -855,7 +860,7 @@ class NewAgent(BaseModel):
executor = self._executors.get(conversation_id)
if executor is None:
return []
return executor.conversation_history
return list(executor.conversation_history)
@property
def conversation_history(self) -> list[Message]:
@@ -863,14 +868,15 @@ class NewAgent(BaseModel):
executor = self._executors.get(self._default_conversation_id)
if executor is None:
return []
return executor.conversation_history
return list(executor.conversation_history)
@property
def last_prompt_stack(self) -> PromptStack | None:
executor = self._executors.get(self._default_conversation_id)
if executor is None:
return None
return executor.prompt_stack
result: PromptStack | None = executor.prompt_stack
return result
@property
def usage_metrics(self) -> dict[str, int]:
@@ -962,6 +968,7 @@ class NewAgent(BaseModel):
)
try:
from crewai.new_agent.executor import _NullPrinter
from crewai.utilities.agent_utils import (
format_message_for_llm,
get_llm_response,
@@ -973,6 +980,7 @@ class NewAgent(BaseModel):
llm=llm,
messages=messages,
callbacks=[],
printer=_NullPrinter(),
)
resolved = str(result).strip()
return resolved if resolved else text

View File

@@ -14,6 +14,7 @@ from uuid import uuid4
from pydantic import BaseModel, Field
from crewai.new_agent.models import Message
from crewai.tools.base_tool import BaseTool
@@ -248,7 +249,8 @@ class SpawnSubtaskTool(BaseTool):
except Exception:
pass
else:
output.append(f"[Subtask {i + 1}] {r.content}")
content = r.content if isinstance(r, Message) else str(r)
output.append(f"[Subtask {i + 1}] {content}")
# GAP-57: Emit spawn completed event
try:
from crewai.new_agent.events import NewAgentSpawnCompletedEvent
@@ -427,7 +429,8 @@ class SpawnSubtaskTool(BaseTool):
except Exception:
pass
else:
results.append(f"[Subtask {i + 1}] {r.content}")
content = r.content if isinstance(r, Message) else str(r)
results.append(f"[Subtask {i + 1}] {content}")
try:
from crewai.new_agent.events import NewAgentSpawnCompletedEvent