"""Timmy's delegation tools — submit tasks and list agents.
Reads agent roster from agents.yaml via the loader module.
No hardcoded agent lists.
"""
import logging
from typing import Any
logger = logging.getLogger(__name__)
def delegate_task(
agent_name: str, task_description: str, priority: str = "normal"
) -> dict[str, Any]:
"""Record a delegation intent to another agent.
Args:
agent_name: Name or ID of the agent to delegate to
task_description: What you want the agent to do
priority: Task priority - "low", "normal", "high"
Returns:
Dict with agent, status, and message
from timmy.agents.loader import list_agents
agent_name = agent_name.lower().strip()
# Build valid agents map from YAML config
available = {a["id"]: a["role"] for a in list_agents()}
if agent_name not in available:
return {
"success": False,
"error": f"Unknown agent: {agent_name}. Valid agents: {', '.join(sorted(available))}",
"task_id": None,
}
valid_priorities = ["low", "normal", "high"]
if priority not in valid_priorities:
priority = "normal"
logger.info(
"Delegation intent: %s → %s (priority=%s)", agent_name, task_description[:80], priority
)
"success": True,
"agent": agent_name,
"role": available[agent_name],
"status": "noted",
"message": f"Delegation to {agent_name} ({available[agent_name]}): {task_description[:100]}",
def list_swarm_agents() -> dict[str, Any]:
"""List all available sub-agents and their roles.
Reads from agents.yaml — no hardcoded roster.
Dict with agent list
try:
agents = list_agents()
"agents": [
{
"name": a["name"],
"id": a["id"],
"role": a["role"],
"status": a.get("status", "available"),
"capabilities": ", ".join(a.get("tools", [])),
for a in agents
],
except Exception as e:
logger.debug("Agent list unavailable: %s", e)
"error": str(e),
"agents": [],
def delegate_to_kimi(task: str, working_directory: str = "") -> dict[str, Any]:
"""Delegate a coding task to Kimi, the external coding agent.
Kimi has 262K context and is optimized for code tasks: writing,
debugging, refactoring, test writing. Timmy thinks and plans,
Kimi executes bulk code changes.
task: Clear, specific coding task description. Include file paths
and expected behavior. Good: "Fix the bug in src/timmy/session.py
where sessions don't persist." Bad: "Fix all bugs."
working_directory: Directory for Kimi to work in. Defaults to repo root.
Dict with success status and Kimi's output or error.
import shutil
import subprocess
from pathlib import Path
from config import settings
kimi_path = shutil.which("kimi")
if not kimi_path:
"error": "kimi CLI not found on PATH. Install with: pip install kimi-cli",
workdir = working_directory or settings.repo_root
if not Path(workdir).is_dir():
"error": f"Working directory does not exist: {workdir}",
cmd = [kimi_path, "--print", "-p", task]
logger.info("Delegating to Kimi: %s (cwd=%s)", task[:80], workdir)
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=300, # 5 minute timeout for coding tasks
cwd=workdir,
output = result.stdout.strip()
if result.returncode != 0 and result.stderr:
output += "\n\nSTDERR:\n" + result.stderr.strip()
"success": result.returncode == 0,
"output": output[-4000:] if len(output) > 4000 else output,
"return_code": result.returncode,
except subprocess.TimeoutExpired:
"error": "Kimi timed out after 300s. Task may be too broad — try breaking it into smaller pieces.",
except Exception as exc:
"error": f"Failed to run Kimi: {exc}",