beadboard/src/app/api/sessions/[beadId]/conversation/route.ts
zenchantlive f3558dc0d1 feat(ui): deliver Social-Dense Agent Sessions Hub
This is our biggest UX pivot of the project. We abandoned the 'Page' model for a 'Command Workspace'.

Triumphs:
- Reclaimed 40% of previously wasted screen real-estate by moving to an auto-filling multi-column grid matrix.
- Built the 'Command Deck'—a high-density header that provides real-time agent presence monitoring at a glance.
- Implemented 'Social Post' cards that map technical protocols to human verbs (e.g., 'Falcon passed mission to Operative-B'), making the audit trail readable for humans.
- Engineered 'Silent Refresh' logic: the feed now appends new activity and comments smoothly without disruptive UI resets or scroll jumps.

Raw Honest Moment:
The original card-based social feed was a failure. It was beautiful in isolation but useless for actual supervision. We had to be honest about the horizontal bloat and rebuild the entire layout foundation from scratch using rem-based fluid units to satisfy the 'War Room' requirement.
2026-02-14 00:20:41 -08:00

58 lines
No EOL
2.1 KiB
TypeScript

import { NextResponse } from 'next/server';
import { activityEventBus } from '../../../../../lib/realtime';
import { getCommunicationSummary } from '../../../../../lib/agent-sessions';
import { readInteractionsViaBd } from '../../../../../lib/read-interactions';
import type { ActivityEvent } from '../../../../../lib/activity';
import type { AgentMessage } from '../../../../../lib/agent-mail';
export const dynamic = 'force-dynamic';
export async function GET(
request: Request,
{ params }: { params: Promise<{ beadId: string }> }
): Promise<Response> {
const { beadId } = await params;
const url = new URL(request.url);
const projectRootSearchParam = url.searchParams.get('projectRoot');
const projectRoot = projectRootSearchParam || process.cwd();
try {
// 1. Get activity events for this bead
const history = activityEventBus.getHistory(projectRoot);
const activity = history.filter((e: ActivityEvent) => e.beadId === beadId);
// 2. Get communication for this bead
const summary = await getCommunicationSummary();
const messages = summary.messages.filter((m: AgentMessage) => m.bead_id === beadId);
// 3. Get local bd interactions via CLI
const beadInteractions = await readInteractionsViaBd(projectRoot, beadId);
// 4. Merge and sort
const thread = [
...activity.map((e: ActivityEvent) => ({
type: 'activity' as const,
id: e.id,
timestamp: e.timestamp,
data: e
})),
...messages.map((m: AgentMessage) => ({
type: 'message' as const,
id: m.message_id,
timestamp: m.created_at,
data: m
})),
...beadInteractions.map(i => ({
type: 'interaction' as const,
id: i.id,
timestamp: i.timestamp,
data: i
}))
].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
return NextResponse.json({ ok: true, thread });
} catch (error) {
console.error('[API/Sessions/Conversation] Failed:', error);
return NextResponse.json({ ok: false, error: String(error) }, { status: 500 });
}
}