Files
oO/ml/agents/focus_area.py
alvis f6b89fc849 refactor(focus-area): output all clusters as context; remove scoring and preferred_areas
The agent no longer picks a winner — it summarises every cluster so the
orchestrator can decide what's relevant. Scoring by overdue count overlapped
with the overdue-task agent. preferred_areas (project-ID based, broken label
matching) removed entirely.

Output format: numbered list of areas with task titles included.
Snapshot: {cluster_count, clusters: [{label, task_count, tasks}]}.
Version bumped to 3.0.0; inferred_params cleared.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 14:57:04 +00:00

66 lines
2.4 KiB
Python

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):
titles = [t.get("content", "").strip() for t in cluster.tasks if t.get("content")]
titles_str = "; ".join(f'"{t}"' for t in titles[:8])
if len(titles) > 8:
titles_str += f" (and {len(titles) - 8} more)"
lines.append(f"{i}. {cluster.label}{cluster.task_count} task(s): {titles_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)