refactor: extract agent bounded context + fix SSE comments + cleanup unused

- Extract src/lib/agent/ bounded context with types, registry, messaging
- Add comments_count to BeadIssue for SSE comment detection
- Create batch endpoints for mail/reservations APIs
- Add memory validation to session-preflight
- Remove unused empty dirs (mockup, sessions, timeline)
- Move stashes to docs/references, gitignore them
This commit is contained in:
zenchantlive 2026-03-04 22:06:40 -08:00
parent 6f41c4af31
commit 18fbafdce4
34 changed files with 62714 additions and 1970 deletions

View file

@ -305,29 +305,24 @@ export function ActivityPanel({ issues, collapsed = false, projectRoot }: Activi
return;
}
const mailResponses = await Promise.all(
agentRoster.map(async (agent) => {
const response = await fetch(`/api/agents/mail?agent=${encodeURIComponent(agent.name)}&limit=15`);
const payload = await response.json().catch(() => ({ ok: false }));
return [agent.name, response.ok && payload.ok ? (payload.data as CoordMessage[]) : []] as const;
}),
);
// Use batch endpoints to reduce API calls from 2N to 2
const agentNames = agentRoster.map(a => a.name).join(',');
const reservationResponses = await Promise.all(
agentRoster.map(async (agent) => {
const response = await fetch(`/api/agents/reservations?agent=${encodeURIComponent(agent.name)}`);
const payload = await response.json().catch(() => ({ ok: false }));
if (!response.ok || !payload.ok) {
return [agent.name, undefined] as const;
}
return [agent.name, payload.data?.reservations?.[0]?.scope as string | undefined] as const;
}),
);
const [mailResponse, reservationsResponse] = await Promise.all([
fetch(`/api/agents/mail/batch?agents=${encodeURIComponent(agentNames)}&limit=15`),
fetch(`/api/agents/reservations/batch?agents=${encodeURIComponent(agentNames)}`),
]);
const mailPayload = await mailResponse.json().catch(() => ({ ok: false, data: [] }));
const reservationsPayload = await reservationsResponse.json().catch(() => ({ ok: false, data: [] }));
// Collect all messages from all agents
const uniqueMessages = new Map<string, CoordMessage>();
for (const [, messages] of mailResponses) {
for (const message of messages) {
uniqueMessages.set(message.message_id, message);
if (mailPayload.ok && mailPayload.data) {
for (const entry of mailPayload.data) {
for (const message of (entry.messages ?? [])) {
uniqueMessages.set(message.message_id, message);
}
}
}
@ -346,8 +341,16 @@ export function ActivityPanel({ issues, collapsed = false, projectRoot }: Activi
.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())
.slice(0, 25);
// Build reservation map
const reservationMap: Record<string, string | undefined> = {};
if (reservationsPayload.ok && reservationsPayload.data) {
for (const entry of reservationsPayload.data) {
reservationMap[entry.agent] = entry.scope;
}
}
setCoordActivities(mapped);
setReservationByAgent(Object.fromEntries(reservationResponses));
setReservationByAgent(reservationMap);
};
void fetchCoordination();

View file

@ -184,38 +184,39 @@ export function SocialPage({
return;
}
const mailPairs = await Promise.all(
agentNames.map(async (agent) => {
const response = await fetch(`/api/agents/mail?agent=${encodeURIComponent(agent)}&limit=25`);
const payload = await response.json().catch(() => ({ ok: false }));
if (!response.ok || !payload.ok) {
return [agent, [] as CoordMessage[]] as const;
}
return [agent, (payload.data ?? []) as CoordMessage[]] as const;
}),
);
// Use batch endpoints to reduce API calls from 2N to 2
const agentsParam = agentNames.join(',');
const reservationsPairs = await Promise.all(
agentNames.map(async (agent) => {
const response = await fetch(`/api/agents/reservations?agent=${encodeURIComponent(agent)}`);
const payload = await response.json().catch(() => ({ ok: false }));
if (!response.ok || !payload.ok) {
return [agent, undefined] as const;
}
const first = (payload.data?.reservations ?? [])[0];
return [agent, first?.scope as string | undefined] as const;
}),
);
const [mailResponse, reservationsResponse] = await Promise.all([
fetch(`/api/agents/mail/batch?agents=${encodeURIComponent(agentsParam)}&limit=25`),
fetch(`/api/agents/reservations/batch?agents=${encodeURIComponent(agentsParam)}`),
]);
const mailPayload = await mailResponse.json().catch(() => ({ ok: false, data: [] }));
const reservationsPayload = await reservationsResponse.json().catch(() => ({ ok: false, data: [] }));
const nextMessages: Record<string, CoordMessage[]> = {};
const nextUnread: Record<string, number> = {};
for (const [agent, messages] of mailPairs) {
nextMessages[agent] = messages;
nextUnread[agent] = messages.filter((m) => m.state === 'unread').length;
const nextReservations: Record<string, string | undefined> = {};
// Process mail results
if (mailPayload.ok && mailPayload.data) {
for (const entry of mailPayload.data) {
nextMessages[entry.agent] = entry.messages ?? [];
nextUnread[entry.agent] = (entry.messages ?? []).filter((m: CoordMessage) => m.state === 'unread').length;
}
}
// Process reservations results
if (reservationsPayload.ok && reservationsPayload.data) {
for (const entry of reservationsPayload.data) {
nextReservations[entry.agent] = entry.scope;
}
}
setAgentMessagesByName(nextMessages);
setAgentUnreadByName(nextUnread);
setAgentReservationsByName(Object.fromEntries(reservationsPairs));
setAgentReservationsByName(nextReservations);
}, [agentNames]);
useEffect(() => {