feat(agent-office): notification broadcast + telegram tracking + activity feed API

- Add WebSocket notification messages for task_assigned/task_completed
- Structure telegram send_message return value with ok/message_id
- Track telegram delivery status in task result_data
- Add test_telegram command to stock agent
- Add GET /api/agent-office/activity unified feed endpoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-11 15:15:01 +09:00
parent cce84de8be
commit de91f424a3
6 changed files with 112 additions and 8 deletions

View File

@@ -259,3 +259,64 @@ def mark_telegram_responded(callback_id: str, action: str) -> None:
"UPDATE telegram_state SET responded=1, action=? WHERE callback_id=?",
(action, callback_id),
)
def get_activity_feed(limit: int = 50, offset: int = 0) -> dict:
with _conn() as conn:
total_row = conn.execute("""
SELECT (SELECT COUNT(*) FROM agent_tasks) + (SELECT COUNT(*) FROM agent_logs) AS total
""").fetchone()
total = total_row["total"] if total_row else 0
rows = conn.execute("""
SELECT 'task' AS type, agent_id, id AS task_id, task_type,
status, NULL AS level,
COALESCE(
json_extract(result_data, '$.summary'),
task_type
) AS message,
created_at, completed_at,
result_data
FROM agent_tasks
UNION ALL
SELECT 'log' AS type, agent_id, task_id, NULL AS task_type,
NULL AS status, level,
message,
created_at, NULL AS completed_at,
NULL AS result_data
FROM agent_logs
ORDER BY created_at DESC
LIMIT ? OFFSET ?
""", (limit, offset)).fetchall()
items = []
for r in rows:
item = {
"type": r["type"],
"agent_id": r["agent_id"],
"task_id": r["task_id"],
"message": r["message"],
"created_at": r["created_at"],
}
if r["type"] == "task":
item["task_type"] = r["task_type"]
item["status"] = r["status"]
item["completed_at"] = r["completed_at"]
if r["created_at"] and r["completed_at"]:
try:
from datetime import datetime
start = datetime.fromisoformat(r["created_at"].replace("Z", "+00:00"))
end = datetime.fromisoformat(r["completed_at"].replace("Z", "+00:00"))
item["duration_seconds"] = round((end - start).total_seconds())
except Exception:
item["duration_seconds"] = None
else:
item["duration_seconds"] = None
result_data = json.loads(r["result_data"]) if r["result_data"] else None
if result_data and "telegram_sent" in result_data:
item["telegram_sent"] = result_data["telegram_sent"]
else:
item["level"] = r["level"]
items.append(item)
return {"items": items, "total": total}