from __future__ import annotations from typing import ClassVar from .base import BaseAgent, AgentInput, AgentOutput from .clustering import cluster_tasks from .manifest import AgentManifest MANIFEST = AgentManifest( id="focus-area", version="3.0.0", # output all clusters as context; no scoring (#129) description="Clusters the user's task list and summarises all areas for the orchestrator.", pref_schema={"type": "object", "additionalProperties": False, "properties": {}}, context_schema=["todoist.tasks"], required_consents=["data:core", "data:todoist"], output_contract={"type": "snippet", "format": "free_text"}, ttl_sec=86_400, inferred_params=[], ) class FocusAreaAgent(BaseAgent): """Clusters tasks and outputs a full area summary for the orchestrator.""" agent_id: ClassVar[str] = MANIFEST.id ttl_seconds: ClassVar[int] = MANIFEST.ttl_sec version: ClassVar[str] = MANIFEST.version # 3.0.0 def compute(self, inp: AgentInput) -> AgentOutput: if not inp.tasks: return self._make_output( inp, "No tasks available to identify focus areas.", {"cluster_count": 0}, ) clusters, new_enrichments = cluster_tasks(inp.tasks, enrichment_cache=inp.enrichment_cache) if not clusters: return self._make_output( inp, "No tasks available to identify focus areas.", {"cluster_count": 0}, ) lines = [f"The user's tasks are grouped into {len(clusters)} area(s):"] for i, cluster in enumerate(clusters, 1): descs = [ t.get("enriched_description") or t.get("content", "") for t in cluster.tasks if t.get("content") ] descs = [d.strip() for d in descs if d.strip()] descs_str = "; ".join(f'"{d}"' for d in descs[:8]) if len(descs) > 8: descs_str += f" (and {len(descs) - 8} more)" lines.append(f"{i}. {cluster.label} — {cluster.task_count} task(s): {descs_str}") lines.append("(Task titles may be in any language — always write the tip in English.)") snapshot = { "cluster_count": len(clusters), "clusters": [ {"label": c.label, "task_count": c.task_count, "tasks": [t.get("content", "") for t in c.tasks]} for c in clusters ], "_new_enrichments": new_enrichments, } return self._make_output(inp, "\n".join(lines), snapshot)