From a4bdcfc8da6133507fc269b65802c8ad77821efa Mon Sep 17 00:00:00 2001 From: Natalie Date: Wed, 20 May 2026 03:36:42 -0700 Subject: [PATCH] =?UTF-8?q?feat(@projects):=20=E2=9C=A8=20enrich=20agent?= =?UTF-8?q?=20fleet=20data=20with=20context?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- src/clare/orchestrator/tools.py | 53 +++++++++++++++++++++++++++++++-- src/clare/web/app.py | 25 ++++++++++++---- 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/clare/orchestrator/tools.py b/src/clare/orchestrator/tools.py index 364572c..3997d93 100644 --- a/src/clare/orchestrator/tools.py +++ b/src/clare/orchestrator/tools.py @@ -584,9 +584,56 @@ def report_status( def list_fleet(conn: sqlite3.Connection) -> dict[str, Any]: - """Snapshot the fleet view: every session's current activity.""" - rows = read.list_agent_status(conn) - return {"agents": [r.model_dump(mode="json") for r in rows]} + """Snapshot the fleet: every session's current activity + context. + + Joins agent_status with sessions, tasks, projects so Clare gets + cwd, host, tmux_name, project-name, task-title in one call. Without + these, Clare's rounds prompt has the agent uuid but no way to + contextualize "what is this agent actually doing?". + """ + rows = conn.execute( + """ + SELECT + ag.session_uuid AS session_uuid, + ag.summary AS summary, + ag.state AS state, + ag.source AS source, + ag.hlc AS hlc, + s.host AS host, + s.cwd AS cwd, + s.tmux_name AS tmux_name, + s.last_triage_priority AS last_triage_priority, + s.last_triage_status AS last_triage_status, + p.name AS project, + t.title AS task_title, + ag.task_id AS task_id + FROM agent_status ag + LEFT JOIN sessions s ON s.uuid = ag.session_uuid + LEFT JOIN tasks t ON t.id = ag.task_id + LEFT JOIN projects p ON p.id = ag.project_id + ORDER BY ag.hlc DESC + """ + ).fetchall() + return { + "agents": [ + { + "session_uuid": r["session_uuid"], + "host": r["host"], + "cwd": r["cwd"], + "tmux_name": r["tmux_name"], + "project": r["project"], + "task_title": r["task_title"], + "task_id": r["task_id"], + "state": r["state"], + "summary": r["summary"], + "source": r["source"], + "last_triage_priority": r["last_triage_priority"], + "last_triage_status": r["last_triage_status"], + "hlc": r["hlc"], + } + for r in rows + ] + } __all__ = [ diff --git a/src/clare/web/app.py b/src/clare/web/app.py index 2c64322..72aca8c 100644 --- a/src/clare/web/app.py +++ b/src/clare/web/app.py @@ -116,11 +116,26 @@ def create_app( return # disabled logger.info("orchestrator rounds loop enabled (every %ds)", interval) prompt = ( - "DO ROUNDS: Call list_fleet, list_recent_events, then summarize " - "(1) what each active agent is working on, (2) any session that " - "looks stuck/idle/blocked, (3) up to 3 concrete next-step " - "recommendations. Reply in 8 short lines max. Before replying, " - "call report_status with your own one-line summary." + "DO ROUNDS. Your job is to REDUCE the user's burden — act when " + "safe, escalate only when judgment is needed.\n" + "\n" + "1. Call `list_fleet` to see every active session + its host, cwd, " + "project, state, summary, source.\n" + "2. Call `list_recent_events` (limit 30) for any project context.\n" + "3. **Take safe actions YOURSELF** — don't ask the user to do " + "what you can do:\n" + " • Sessions whose agent_status is stale (source=triage or " + "no recent push) and look idle/blocked → `send_to_session " + "session_ref= text='[probe] one-line status?'`\n" + " • Sessions reporting `done` work → `transition_task_state` " + "if assigned to a task.\n" + " • Recurring patterns across sessions (e.g. all hitting the " + "same git rebase error) → ONE broadcast with the fix.\n" + "4. Always call `report_status` with your own summary.\n" + "5. Reply with one short paragraph PER PROJECT showing 3 " + "concrete next steps the user could take (or saying 'nothing " + "needed, handled X auto-actions'). Skip projects with no live " + "work. Cap reply at 25 lines total." ) while True: try: