Migration Guide — v1.3.29¶
This release introduces two admin-chat-parity and dashboard-parity features that clean up long-standing inconsistencies between the Telegram transport, the admin panel, and the mobile clients. Most changes are additive, but the dashboard / system status refactor drops several response fields — any caller that still reads them needs an update.
Core version: 1.3.29 — see
pyproject.toml.
At a glance¶
| Area | Change | Breaking? |
|---|---|---|
GET /api/system/status | agents, sessions, cron_jobs, tasks_total, tasks_active, agent_count, active_tasks_count removed | Yes |
GET /api/dashboard/summary | New — canonical dashboard payload with localized activity | No (additive) |
GET /api/activity/recent | New — localized, typed activity feed | No (additive, legacy /api/activity kept) |
GET /api/activity/{id}?verbose=1 | New — single-event lookup with raw source | No (additive) |
| WebSocket streaming frames | Now carry session_id + agent | Soft-breaking for clients that ignored/asserted their absence |
WebSocket chat_message event | New envelope type | No (additive) |
Accept-Language on dashboard/activity | Server-side localized strings | No |
1. /api/system/status — removed counters¶
What changed¶
Prior to 1.3.29 /api/system/status returned both machine metrics and a bag of counters for the dashboard:
// OLD response
{
"cpu": {...},
"ram": {...},
"disk": {...},
"uptime_seconds": 12345,
"uptime_human": "3h 25m",
"agents": 4, // <- removed
"agent_count": 4, // <- removed (legacy alias)
"sessions": 12, // <- removed
"cron_jobs": 7, // <- removed
"tasks_total": 150, // <- removed
"tasks_active": 2, // <- removed
"active_tasks_count": 2 // <- removed (legacy alias)
}
Counters depended on live state from several subsystems (agents.json, tasks.json, cron_jobs.json, session registry), which meant every request touched all of them and cache invalidation was impossible to reason about. Dashboard refresh also triggered a fan-out of 3–4 parallel REST calls.
What to do¶
Read counters from /api/dashboard/summary.counters instead:
// GET /api/dashboard/summary → counters block
{
"counters": {
"agents_total": 4,
"agents_online": 2,
"active_tasks": 2,
"running_crons": 0,
"failed_last_24h": 1
}
}
Field mapping:
Old (/system/status) | New (/dashboard/summary.counters) |
|---|---|
agents / agent_count | agents_total (plus new agents_online) |
tasks_active / active_tasks_count | active_tasks |
cron_jobs | (not exposed directly — use /api/cron or inspect running_crons for currently-firing jobs) |
sessions | (not exposed — use /api/sessions if you need the list) |
tasks_total | (not exposed — use /api/tasks?limit=…) |
/api/system/status still returns CPU / RAM / disk / uptime, so any caller that only used those fields needs no changes.
2. /api/dashboard/summary — new endpoint¶
One-shot payload for the dashboard page; replaces the previous /agents + /system/status + /activity + /cron fan-out.
Response:
{
"system": {
"cpu": {...}, "ram": {...}, "disk": {...},
"uptime_seconds": 12345,
"uptime_human": "3 ч 25 мин" // localized
},
"counters": {
"agents_total": 4, "agents_online": 2,
"active_tasks": 2, "running_crons": 0, "failed_last_24h": 1
},
"recent_activity": [
{
"id": "evt-...",
"type": "task_completed", // machine-typed for filtering
"title": "Задача «Flight search» завершена",
"subtitle": "2 мин назад • main",
"agent_name": "main",
"timestamp": "2026-04-19T01:05:00Z",
"severity": "success"
}
]
}
Notes:
Accept-Language: ru|encontrols title/subtitle strings. Default isru.- Server-side 5 second cache per
(language, ACL bucket)absorbs refresh spikes. recent_activityis always the 10 latest events visible to the caller; to page further, use/api/activity/recent.- ACL filtering: if the caller has
allowed_agentsset, bothcounters.agents_totalandrecent_activityonly include events for those agents.
See admin-panel.md — System endpoints for the reference table.
3. /api/activity/recent — canonical activity stream¶
Every event carries:
| Field | Type | Purpose |
|---|---|---|
id | string | Stable event id — usable with /api/activity/{id} |
type | enum | Machine type (for filtering): task_completed, task_failed, agent_started, agent_stopped, cron_fired, cron_failed, webhook_called, auth_login |
title | localized string | Human-readable summary. Never raw AGENT_TASK_FINISHED_OK-style codes |
subtitle | localized string | Relative time + agent |
agent_name | string|null | |
timestamp | ISO-8601 UTC | |
severity | enum | info / success / warning / error |
Unknown machine types receive a localized fallback title — raw machine strings are never leaked to the UI. /api/activity/{id}?verbose=1 adds the raw source record + a source field (notifications, tasks, cron, audit) for admin debugging.
Legacy /api/activity¶
The old /api/activity endpoint is kept as-is for backward compatibility — it still returns raw type / message pairs. New clients should use /api/activity/recent; the old one will not get new features.
4. WebSocket streaming — session_id + agent on every frame¶
What changed¶
Pre-1.3.29, streaming frames on /ws/admin looked like:
Now every streaming frame carries the originating chat session and agent:
This applies to:
text_deltatool_activitysystem_statusresulterror
Why¶
Admin WS connections are now registered per-user (keyed on the JWT sub claim), so a streaming turn is fanned out to every live socket of that user (other tabs, mobile PWA, etc.). Without session_id / agent, sibling tabs cannot route events to the correct chat when the user has different sessions open on different devices.
What to do¶
Clients that ignore unknown fields need no changes. Clients that asserted field shapes (strict schema validators, TypeScript type-guards) must widen their types. Reference types live in:
- Admin:
sygen-admin/src/lib/websocket.ts - Mobile: the Onyx shared KMP model set
- Full event catalog: admin-panel.md — WebSocket Protocol
Routing rule on the client:
if (frame.session_id && frame.session_id !== activeSessionId(frame.agent)) {
// Update history for frame.session_id in-place, but don't swap the visible chat.
updateSessionInBackground(frame.agent, frame.session_id, frame);
return;
}
render(frame);
5. New WebSocket event: chat_message¶
Telegram-transport deliveries are now mirrored into the admin chat.
{
"type": "chat_message",
"kind": "task_result",
"role": "agent",
"agent": "main",
"session_id": "sess-abc",
"content": "...",
"meta": { "task_id": "t-1", "task_name": "Flight search" },
"timestamp": 1700000000
}
kind is one of:
task_result— a background task finishedtask_question— a task asked a question viaask_parent.pyinteragent— response fromask_agent/ask_agent_asynctext— a plain background message
These are persisted server-side via sygen_bot/api/chat_history.py and attached to the agent's most recently updated REST chat session (fallback when the envelope has no named session id), so a client reload still sees them.
Action required¶
- Clients that render WS events must handle the new
chat_messagetype explicitly. If ignored, the live view will miss messages the REST history has — a reload will suddenly surface them. - No REST changes are needed: existing
GET /api/chat/sessions/{id}/messagesalready returns them.
6. Mirrors / cross-device sync¶
The admin WS registry (_user_ws) now maintains a per-user map of live sockets. When any socket of a user receives a streaming event, it is also pushed to every sibling socket (excluding the originator), respecting per-socket agent ACLs.
Impact on clients:
- A user with dashboard open on desktop + PWA will see the same live stream on both.
- An inactive tab will receive events it did not initiate — this is intentional and keeps chat state consistent across devices.
- Broadcasts are ACL-aware: a sibling socket that is not allowed to see a given agent receives nothing.
Client checklist¶
- [ ] Any caller reading
agent_count/active_tasks_count/agents/tasks_activeon/api/system/status→ migrate to/api/dashboard/summary.counters - [ ] Dashboard fan-outs to
/agents+/system/status+/activity→ collapse into a single/api/dashboard/summarycall - [ ] WebSocket event handler accepts optional
session_id+agentontext_delta/tool_activity/system_status/result/error - [ ] WebSocket event handler routes
chat_messageenvelopes into the session store - [ ] Activity page points at
/api/activity/recentand renderstitle+subtitle; rawtypeis only used for filtering - [ ]
Accept-Languageis set on dashboard / activity requests if the UI is non-Russian
References¶
- Admin Panel REST + WS reference
- sygen-admin README — the admin panel itself already targets 1.3.29