Get the current status of all AI team members for an entire project. Status is derived from the activities table and scoped to the project across all conversations. Only authenticated users with access to the project can call this endpoint.
- The endpoint is project-scoped — it returns team status across all conversations in the project.
- Mara (talkide-pm) is always present in the response, even if no activities have been recorded yet (placeholder, status
IDLE). - All other members (Iris, Theo, Eli, Nia, Kai) appear in the response only after their first activity of any type within the project.
- Status
WORKINGmeans there is aTASK_STARTEDactivity for thatagent_rolewithin the project that has no subsequent matchingTASK_COMPLETED. - Status
IDLEmeans all tasks started by the agent have a matchingTASK_COMPLETED, or the agent has no activities at all. currentTask=descriptionof the most recentTASK_STARTEDwithout aTASK_COMPLETEDfor that agent;nullifIDLE.lastActiveAt=created_atof the most recent activity row for that agent;nullfor Mara if she has no activities yet.- This endpoint is a snapshot — for live updates the frontend subscribes to the SSE stream (UC-05001) and updates the team status panel incrementally.
Agent Role Mapping
agent_role (technical key) | Name | Label |
|---|---|---|
talkide-pm | Mara | Product Manager |
talkide-analyst | Iris | Analyst |
talkide-backend-dev | Theo | Backend Engineer |
talkide-frontend-dev | Eli | Frontend Engineer |
talkide-reviewer | Nia | Reviewer |
talkide-devops | Kai | DevOps |
sequenceDiagram
actor User
User->>+FE: opens workspace view
FE->>+BE: GET /api/v1/projects/{projectId}/team <br> Authorization: Bearer {accessToken}
BE->>BE: validate access token
alt token missing or invalid
BE-->>FE: 401 Unauthorized <br> ErrorResponse
end
BE->>DB: load project by projectId
alt project not found
BE-->>FE: 404 Not Found <br> ErrorResponse
end
BE->>BE: check project belongs to user's tenant
alt project does not belong to tenant
BE-->>FE: 403 Forbidden <br> ErrorResponse
end
BE->>DB: query latest TASK_STARTED without TASK_COMPLETED per agent_role <br> and latest activity created_at per agent_role <br> scoped to project_id
BE->>BE: always include talkide-pm (Mara) even if no activities <br> include other roles only if they have at least one activity
BE->>-FE: 200 OK <br> TeamStatusResponse
FE->>-User: render team status panel
GET /api/v1/projects/{projectId}/team
200 OK TeamStatusResponse (project has activity from Mara and Eli; Iris, Theo, Nia, Kai have no activities yet):
{
"members": [
{
"agentRole": "talkide-pm",
"name": "Mara",
"label": "Product Manager",
"status": "WORKING",
"currentTask": "Plánuje aplikaci",
"lastActiveAt": "2026-04-30T11:24:30Z"
},
{
"agentRole": "talkide-frontend-dev",
"name": "Eli",
"label": "Frontend Engineer",
"status": "WORKING",
"currentTask": "Pozadí aplikace",
"lastActiveAt": "2026-04-30T11:24:00Z"
}
]
}
200 OK TeamStatusResponse (empty project — no activities at all):
{
"members": [
{
"agentRole": "talkide-pm",
"name": "Mara",
"label": "Product Manager",
"status": "IDLE",
"currentTask": null,
"lastActiveAt": null
}
]
}
401 Unauthorized (missing or invalid access token) ErrorResponse:
{
"status": 401,
"code": "AUTHENTICATION_FAILED",
"message": "Access token is missing or invalid"
}
403 Forbidden (project does not belong to user’s tenant) ErrorResponse:
{
"status": 403,
"code": "FORBIDDEN",
"message": "You do not have access to this project"
}
404 Not Found (project not found) ErrorResponse:
{
"status": 404,
"code": "NOT_FOUND_PROJECT",
"message": "Project not found"
}
Team Status Rules
| Condition | Status | currentTask | lastActiveAt |
|---|---|---|---|
Agent has a TASK_STARTED with no matching TASK_COMPLETED | WORKING | description of that TASK_STARTED | created_at of most recent activity |
| Agent has activities but all tasks are completed | IDLE | null | created_at of most recent activity |
| Agent has no activities at all (other than Mara) | not included in response | — | — |
Mara (talkide-pm) has no activities | IDLE | null | null |
UX Guidelines
Context
Team Status Strip je horizontální pruh v horní části levého sloupce workspace (chat column), hned pod TTopBar. V současnosti je hardcoded přes TEAM_BY_ID data z team.ts a zobrazuje 4 členy. Po refactoru bude řízen daty z GET /team + live update přes SSE eventy (UC-05001). Existující komponenta TeamChip.vue se zachovává a rozšíří o currentTask label a animace.
User Flow
- Workspace se načte → FE zavolá
GET /projects/{projectId}/team. - Strip zobrazí skeleton (jeden placeholder chip).
- Po odpovědi se zobrazí Mara (vždy první) + ostatní členové podle pořadí prvního výskytu.
- SSE event
activityseventType=TASK_STARTED→ FE přidá/aktualizuje chip dotčeného agenta na WORKING. - SSE event
activityseventType=TASK_COMPLETED→ FE přepne chip na IDLE a skryjecurrentTask. - Hover nad chipem → tooltip s detailem (jméno, label, stav, currentTask, lastActiveAt).
- (Future enhancement) Klik na chip → filtruje activity feed na daného agenta.
Layout — Team Strip
┌─────────────────────────────────────────────────────────────┐
│ ON IT [Mara ●] [Eli ●] [Iris ●] [Theo ○] [Nia ○] │
└─────────────────────────────────────────────────────────────┘
- Kontejner:
px-5 py-3.5 border-b border-[var(--line-1)] flex items-center gap-3 shrink-0(stávající) - Label “ON IT”:
text-[11px] font-mono uppercase text-[var(--fg-3)]sletter-spacing: 0.06em(stávající) - Chipy:
flex gap-2 flex-1— základní wrap zakázán; pokud chipů > 5–6, strip máoverflow-x: auto(vodorovný scroll) se skrytou scrollbar - Mara je vždy na pozici 0 (první zleva)
- Ostatní členové v pořadí podle
lastActiveAtDESC (= pořadí prvního výskytu, nejdříve aktivní = vlevo)
Chip Anatomy
TeamChip.vue — stávající props: member, status: 'leading' | 'working' | 'idle', task?: string
Po refactoru (props se rozšíří, nezmění se):
statusprop dostane hodnotu'leading'pro Maru (vždy),'working'pokudstatus === 'WORKING','idle'pokudstatus === 'IDLE'taskprop =currentTaskz API (string nebo null)
Vizuální anatomie chipu (stávající layout zachován):
┌────────────────────────────────────┐
│ ┌───┐ │
│ │ E │● jméno · currentTask… │
│ └───┘ │
└────────────────────────────────────┘
| Slot | Popis |
|---|---|
| Avatar | TAvatar 20px, barevná inicializace z agentColor |
| Status dot | 8px kruh absolute -right-0.5 -bottom-0.5; WORKING/leading = glow (box-shadow: 0 0 6px <color>); IDLE = var(--fg-4) bez glow |
| Agent name | text-xs — vždy viditelný |
| CurrentTask | text-[11px] text-[var(--fg-3)] — prefixed · — zobrazeno jen pokud task prop není null; truncate na ~25 znaků s ellipsis |
Status dot colors (konzistentní s TeamChip.vue):
| Stav | Barva | Glow |
|---|---|---|
leading (Mara) | amber var(--amber) | ano |
working | indigo var(--indigo) | ano |
idle | var(--fg-4) | ne |
States
WORKING
- Status dot: barevný + pulsující (CSS animation
pulse2s infinite — stejný pattern jako u pulsujícího dotu v activity feedu) currentTaskprop je vyplněný — zobrazí se vedle jména s·oddělovačem- Tooltip zobrazí plnou hodnotu
currentTask(pokud je text truncated)
IDLE
- Status dot: šedý
var(--fg-4), bez glow, bez animace taskprop je null —currentTasklabel se nezobrazuje- Chip je mírně ztlumený:
opacity-70pro IDLE členy (Mara jakoleadingmá vždy 100 %)
Initial / Empty
Zobrazí se jen Mara s status="leading" a bez task. Ostatní chipy se neukážou dokud nemají activity.
Tooltip on Hover
Na hover nad chipem se zobrazí floating tooltip (vlastní nebo knihovní — implementace na devs):
┌─────────────────────────────┐
│ Eli │
│ Frontend Engineer │
│ ───────────────────────── │
│ Stav: Pracuje │
│ Úkol: Pozadí aplikace │
│ Naposledy aktivní: 2 min │
└─────────────────────────────┘
- Pozice: nad chipem (nebo pod pokud u horního okraje), centered horizontálně
- Background:
var(--bg-3), bordervar(--line-1),rounded-lg,shadow-lg - Delay: 400 ms hover delay (neotevírá se na přejetí myší)
lastActiveAt: relativní čas stejného formátu jako v activity feedu- Pokud
status === 'IDLE': řádek “Úkol” se nezobrazuje
Animations
Nový člen přibyde (IDLE → první výskyt)
Chip se přidá na konec řady (za poslední stávající chip):
- Vloží se s
opacity-0 scale-90 - Transition na
opacity-100 scale-100— 300 ms ease-out - Sousední chipy se posunou plynule (layout shift, ne transform)
Přechod IDLE → WORKING
- Status dot: barva přejde z
var(--fg-4)navar(--indigo)— color transition 500 ms - Glow se rozsvítí (box-shadow transition 500 ms)
currentTasklabel se objeví sopacity-0 → opacity-100— 300 ms fade-in (po 200 ms délace, aby dot animace proběhla první)- Chip se mírně rozšíří (width transition přes
max-widthnebo Tailwindtransition-all)
Přechod WORKING → IDLE
currentTasklabel zfadujeopacity-100 → opacity-0— 200 ms- Status dot: barva přejde na
var(--fg-4)— transition 500 ms - Glow zhasne (box-shadow transition 500 ms)
- Chip se zúží (width transition)
Všechny transition třídy: transition-all duration-500 ease-in-out na chip kontejneru.
Accessibility
- Strip jako celek:
role="list" aria-label="Team status"(nebo česky dle aktivního locale) - Každý chip:
role="listitem" - Avatar inicializace:
aria-label="{agentName}"(text reprezentace) - Status dot:
aria-hidden="true"(dekorativní) - Tooltip:
role="tooltip"+ chip máaria-describedby="{tooltip-id}" - Tab order: chipy jsou focusable (
tabindex="0"); Enter/Space = stejná akce jako klik (future: filter)
i18n Keys
| Klíč | EN | CS |
|---|---|---|
workspace.team.status.working | Working | Pracuje |
workspace.team.status.idle | Idle | Čeká |
workspace.team.status.leading | Leading | Řídí |
workspace.team.tooltip.currentTask | Task | Úkol |
workspace.team.tooltip.lastActive | Last active | Naposledy aktivní |
workspace.team.tooltip.noActivity | No activity yet | Zatím žádná aktivita |
workspace.team.stripLabel | On it | Na tom |
workspace.team.aria.strip | Team status | Stav týmu |
Poznámka:
workspace.team.stripLabelodpovídá stávajícímu hardcodedt('workspace.onIt')— při refactoru stačí přesunout pod nový klíč nebo ponechat stávající klíč.
Frontend
Validations
| Field | Constraints | Size | Pattern | Note |
|---|---|---|---|---|
| projectId | (path) | — | — | Required path segment |
Backend
Validations
| Field | Constraints | Size | Pattern | Note |
|---|---|---|---|---|
| projectId | not_null, positive | — | — | Path variable; must reference an existing project belonging to user’s tenant |
Test Cases
| GIVEN | WHEN | THEN |
|---|---|---|
| project exists with no activities | GET /api/v1/projects/{projectId}/team is called | 200 OK; response contains exactly one member: Mara with status=IDLE, currentTask=null, lastActiveAt=null |
| Mara delegates task to Eli with description “Pozadí aplikace” (TASK_STARTED inserted for talkide-frontend-dev, no TASK_COMPLETED yet) | GET /api/v1/projects/{projectId}/team is called | 200 OK; Mara WORKING + Eli WORKING with currentTask=“Pozadí aplikace”; Iris/Theo/Nia/Kai absent |
| Eli’s TASK_STARTED has a matching TASK_COMPLETED | GET /api/v1/projects/{projectId}/team is called | 200 OK; Eli status=IDLE, currentTask=null, lastActiveAt updated to TASK_COMPLETED created_at |
| Mara delegates a second task to Eli with description “Tlačítko odeslat” (new TASK_STARTED, no TASK_COMPLETED) | GET /api/v1/projects/{projectId}/team is called | 200 OK; Eli status=WORKING, currentTask=“Tlačítko odeslat” (most recent open task description) |
| Mara delegates to Iris (TASK_STARTED for talkide-analyst) and Eli (TASK_STARTED for talkide-frontend-dev) simultaneously, neither has TASK_COMPLETED | GET /api/v1/projects/{projectId}/team is called | 200 OK; Mara + Iris + Eli all WORKING; Theo/Nia/Kai absent |
| authenticated user, project belongs to a different tenant | GET /api/v1/projects/{projectId}/team is called | 403 FORBIDDEN error response is returned |
| authenticated user, project does not exist | GET /api/v1/projects/{projectId}/team is called | 404 NOT_FOUND_PROJECT error response is returned |
| no Authorization header | GET /api/v1/projects/{projectId}/team is called | 401 AUTHENTICATION_FAILED error response is returned |
Thanks for the feedback.