cc-haha/desktop/src/api/teams.ts
程序员阿江(Relakkes) 23b31b397a Prevent local team sessions from dropping members and stalling adapters
This bundles the pending desktop/server team-session fixes with the local adapter recovery changes already in the worktree. The team path now keeps teammate membership stable under concurrent spawns, surfaces real teammate identities in the desktop UI, and allows direct interaction with member transcripts. The adapter changes recover automatically when stale thinking signatures invalidate an existing session.

Constraint: Team config writes can happen concurrently while multiple reviewers spawn in parallel

Constraint: Desktop member views must follow mailbox/transcript semantics rather than hijacking teammate runtime sessions

Rejected: Keep relying on config.json alone for member discovery | in-process teammates can be lost after concurrent writes

Rejected: Open teammate sessionIds as normal desktop sessions | would attach a second CLI instead of the running teammate

Confidence: medium

Scope-risk: moderate

Reversibility: clean

Directive: Preserve locked team-file mutation for any future teammate registration path and keep teammate labels sourced from member names before agent types

Tested: bun test src/server/__tests__/teams.test.ts src/server/__tests__/team-watcher.test.ts

Tested: cd desktop && bun run test --run src/stores/chatStore.test.ts src/pages/ActiveSession.test.tsx

Tested: cd desktop && bun run lint

Tested: cd desktop && bun run build

Not-tested: Manual end-to-end validation against a live Agent Teams run in the desktop app
2026-04-14 17:27:07 +08:00

45 lines
1.1 KiB
TypeScript

import { api } from './client'
import type { TeamSummary, TeamDetail } from '../types/team'
type TeamsResponse = { teams: TeamSummary[] }
type TranscriptMessage = {
id: string
type: string
content: unknown
timestamp: string
model?: string
parentToolUseId?: string
}
type TranscriptResponse = { messages: TranscriptMessage[] }
export type { TranscriptMessage }
export const teamsApi = {
list() {
return api.get<TeamsResponse>('/api/teams')
},
get(name: string) {
return api.get<TeamDetail>(`/api/teams/${encodeURIComponent(name)}`)
},
getMemberTranscript(teamName: string, agentId: string) {
return api.get<TranscriptResponse>(
`/api/teams/${encodeURIComponent(teamName)}/members/${encodeURIComponent(agentId)}/transcript`,
)
},
sendMemberMessage(teamName: string, agentId: string, content: string) {
return api.post<{ ok: true }>(
`/api/teams/${encodeURIComponent(teamName)}/members/${encodeURIComponent(agentId)}/messages`,
{ content },
)
},
delete(name: string) {
return api.delete<{ ok: true }>(`/api/teams/${encodeURIComponent(name)}`)
},
}